为什么Math.sin()委托给StrictMath.sin()?
我想知道,当我在Reddit线程中发现问题时,为什么Math.sin(double)
委托给StrictMath.sin(double)
。 提到的代码片段如下所示(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);
第二个声明是native
,对我来说是合理的。 Math
指出:
鼓励代码生成器使用特定于平台的本机库或微处理器指令(如果可用)
问题是 :是不是本机库实现了StrictMath
平台特定的? JIT比安装的JRE更了解平台(请专注于这种情况)? 换句话说,为什么Math.sin()
本身不是原生的?
我将尝试在一篇文章中结束整个讨论。
通常, Math
委托给StrictMath
。 显然,可以内联调用,因此这不是性能问题。
StrictMath
是一个最终类,其native
方法由本机库支持。 有人可能认为, 本土意味着最优 ,但并非必须如此。 通过StrictMath
javadoc可以阅读以下内容:
(…)此程序包中某些数字函数的定义要求它们产生与某些已发布算法相同的结果。 这些算法可以从众所周知的网络库netlib获得,作为“Freely Distributable Math Library”包fdlibm。 然后,这些以C编程语言编写的算法将被理解为遵循Java浮点算法规则的所有浮点运算来执行。
我如何理解这个文档是实现StrictMath
的本机库是用fdlibm库实现的, fdlibm库是多平台的并且已知可以产生可预测的结果。 因为它是多平台的,所以不能期望它是每个平台上的最佳实现, 我相信这是智能JIT可以微调实际性能的地方,例如通过输入范围的统计分析和调整算法/相应地实施。
深入研究实现很快就会发现,备份StrictMath
的本机库实际上使用了 fdlibm :
OpenJDK 7中的StrictMath.c源代码如下所示:
#include "fdlibm.h" ... JNIEXPORT jdouble JNICALL Java_java_lang_StrictMath_sin(JNIEnv *env, jclass unused, jdouble d) { return (jdouble) jsin((double)d); }
并且sine函数在fdlibm / src / s_sin.c中定义,在几个地方引用直接来自头文件fdlibm.h的 __kernel_sin
函数。
虽然我暂时接受了自己的答案 ,但我很高兴接受一个更有能力的答案 。
为什么Math.sin()委托给StrictMath.sin()?
JIT编译器应该能够内联StrictMath.sin(a)
调用。 因此,为Math.sin()
案例创建额外的native
方法并添加额外的JIT编译器智能来优化调用序列等等,这一点毫无意义。
有鉴于此,你的反对意见真的归结为一个“优雅”的问题。 但“务实”的观点更具说服力:
-
较少的本机调用使JVM核心和JIT更易于维护,更不易碎,等等。
-
如果没有损坏,请不要修理它。
至少,这就是我想象 Java团队如何看待它。
该问题假定JVM实际上运行委托代码。 在许多JVM上,它不会。 对Math.sin()等的调用可能会被JIT替换为透明的一些内在函数代码(如果合适)。 这通常以不可观察的方式对最终用户完成。 对于可能发生有趣特化的JVM实现者来说,这是一个常见的技巧(即使该方法未标记为本机)。
但请注意,由于合适的输入范围,大多数平台不能简单地插入单处理器的sin指令(例如:参见: 英特尔讨论 )。
Math API允许其方法的非严格但性能更好的实现,但不需要它,默认情况下,Math只使用StrictMath impl。