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