24

I was wondering, why does Math.sin(double) delegate to StrictMath.sin(double) when I've found the problem in a Reddit thread. The mentioned code fragment looks like this (JDK 7u25):

Math.java :

public static double sin(double a) {
    return StrictMath.sin(a); // default impl. delegates to StrictMath
}

StrictMath.java :

public static native double sin(double a);

The second declaration is native which is reasonable for me. The doc of Math states that:

Code generators are encouraged to use platform-specific native libraries or microprocessor instructions, where available (...)

And the question is: isn't the native library that implements StrictMath platform-specific enough? What more can a JIT know about the platform than an installed JRE (please only concentrate on this very case)? In ther words, why isn't Math.sin() native already?

emesx
  • 12,555
  • 10
  • 58
  • 91
  • possible duplicate of [What's the difference between java.lang.Math and java.lang.StrictMath?](http://stackoverflow.com/questions/4232231/whats-the-difference-between-java-lang-math-and-java-lang-strictmath) – Ruchira Gayan Ranaweera Jul 01 '13 at 17:58
  • 1
    @Ruchira as you can read in my question, I'm concentrating on something different than the precision of computation (which **is** described in the docs) – emesx Jul 01 '13 at 18:00

4 Answers4

17

I'll try to wrap up the entire discussion in a single post..

Generally, Math delegates to StrictMath. Obviously, the call can be inlined so this is not a performance issue.

StrictMath is a final class with native methods backed by native libraries. One might think, that native means optimal, but this doesn't necessarily has to be the case. Looking through StrictMath javadoc one can read the following:

(...) the definitions of some of the numeric functions in this package require that they produce the same results as certain published algorithms. These algorithms are available from the well-known network library netlib as the package "Freely Distributable Math Library," fdlibm. These algorithms, which are written in the C programming language, are then to be understood as executed with all floating-point operations following the rules of Java floating-point arithmetic.

How I understand this doc is that the native library implementing StrictMath is implemented in terms of fdlibm library, which is multi-platform and known to produce predictable results. Because it's multi-platform, it can't be expected to be an optimal implementation on every platform and I believe that this is the place where a smart JIT can fine-tune the actual performance e.g. by statistical analysis of input ranges and adjusting the algorithms/implementation accordingly.

Digging deeper into the implementation it quickly turns out, that the native library backing up StrictMath actually uses fdlibm:

StrictMath.c source in OpenJDK 7 looks like this:

   #include "fdlibm.h"
   ...
   JNIEXPORT jdouble JNICALL
   Java_java_lang_StrictMath_sin(JNIEnv *env, jclass unused, jdouble d)
   {
       return (jdouble) jsin((double)d);
   }

and the sine function is defined in fdlibm/src/s_sin.c refering in a few places to __kernel_sin function that comes directly from the header fdlibm.h.


While I'm temporarily accepting my own answer, I'd be glad to accept a more competent one when it comes up.

dantiston
  • 5,161
  • 2
  • 26
  • 30
emesx
  • 12,555
  • 10
  • 58
  • 91
4

Why does Math.sin() delegate to StrictMath.sin()?

The JIT compiler should be able to inline the StrictMath.sin(a) call. So there's little point creating an extra native method for the Math.sin() case ... and adding extra JIT compiler smarts to optimize the calling sequence, etcetera.

In the light of that, your objection really boils down to an "elegance" issue. But the "pragmatic" viewpoint is more persuasive:

  • Fewer native calls makes the JVM core and JIT easier to maintain, less fragile, etcetera.

  • If it ain't broken, don't fix it.

At least, that's how I imagine how the Java team would view this.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • While being true, I was hoping for a different answer. I totally understand how a JIT can optimize the code on-the-fly, but I don't know what can it do in terms of **platform-specific** optimizations that a native lib, compiled for that platform can't (*because this is what the docs seem to suggest*). – emesx Jul 01 '13 at 18:24
  • This is a case where you don't need such a capability. But there may be other cases where you do. You could try deep-diving the JIT compiler source code to look for platform specific optimizations ... :-) – Stephen C Jul 01 '13 at 18:26
  • 1
    "I was hoping for a different answer" - sadly that's often the way. I agree with Stephen. Whilst there may be cases where platform-specific JIT optimisations are going on it doesn't look like it's here. – selig Jul 01 '13 at 22:09
  • 1
    @selig Can you give an example of a case where it (*JIT being more platform-specific than native lib*) takes place? – emesx Jul 02 '13 at 04:53
  • @elmes - He said "may be cases", not "are cases". That implies that he doesn't know of actual cases. (And neither do I.) But as I said above, if you really want to find out, download and read the OpenJDK source code. Please don't expect other people to do your research for you. – Stephen C Jul 02 '13 at 07:29
  • 1
    I meant I do appreciate your input, please don't get me wrong! Some people have pointed out to scan through some sources in the OpenJDK and they indeed use some `do_intrinsic()` macros messing around with `Math.sin`.. but I guess that's the most I can uderstand from it. Personally, I find the `Math` -> `StrictMath` delegation unnecessary, superfluous in the presence of such a JavaDoc comment (the one I quoted), even when the call is optimized away.. – emesx Jul 02 '13 at 18:24
  • It is no unnecessary or superfluous. It is just a different way of implementing the specified behaviour. From the perspective of the normal Java programmer, the difference is irrelevant. From the Java team's perspective the delegation approach is arguably BETTER because it makes the JVM core easier to maintain. And that is important. Anyway, until you really understand how intrinsics are handled, you are not in a good position to be critiquing this aspect of the codebase. – Stephen C Jul 02 '13 at 22:31
4

The question assumes that the JVM actually runs the delegation code. On many JVMs, it won't. Calls to Math.sin(), etc.. will potentially be replaced by the JIT with some intrinsic function code (if suitable) transparently. This will typically be done in an unobservable way to the end user. This is a common trick for JVM implementers where interesting specializations can happen (even if the method is not tagged as native).

Note however that most platforms can't simply drop in the single processor instruction for sin due to suitable input ranges (eg see: Intel discussion).

Trent Gray-Donald
  • 2,286
  • 14
  • 17
  • I'll explain my question again: `StrictMath` is native, so it uses some platform-specific low-level libraries by definition.. `Math` delegates to `StrictMath` by default (it's obvious that the call can be optimized away) stating, that the delegation **should** be replaced with some **platform-specific** code. What more platform-specific can a JIT do, that a proper native library can't? – emesx Jul 09 '13 at 15:26
  • So StringMath is basically a passthrough to fdlibm native code, which is very platform-agnostic but predictable about answers. I know you know that :-) Platforms could replace the code in Math with code that diverges by a small number of ULPs. How it is physically replaced is up to the JIT (or class file loader) but to give an example of a real-world in on Intel one could (for some very specific input number ranges) use the fsin instruction much more directly. If the JIT can prove (via live program analysis) the input is always in that safe range, it could do that and deliver a huge speedup. – Trent Gray-Donald Jul 10 '13 at 15:19
1

Math API permits a non-strict but better-performing implementations of its methods but does not require it and by default Math simply uses StrictMath impl.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275