Java JIT 在运行 JDK 代码时会作弊吗?
我正在对一些代码进行基准测试,但我无法让它像使用 java.math.BigInteger
,即使使用完全相同的算法.所以我复制了 java.math.BigInteger代码> 源代码到我自己的包中并尝试了这个:
I was benchmarking some code, and I could not get it to run as fast as with java.math.BigInteger
, even when using the exact same algorithm.
So I copied java.math.BigInteger
source into my own package and tried this:
//import java.math.BigInteger;
public class MultiplyTest {
public static void main(String[] args) {
Random r = new Random(1);
long tm = 0, count = 0,result=0;
for (int i = 0; i < 400000; i++) {
int s1 = 400, s2 = 400;
BigInteger a = new BigInteger(s1 * 8, r), b = new BigInteger(s2 * 8, r);
long tm1 = System.nanoTime();
BigInteger c = a.multiply(b);
if (i > 100000) {
tm += System.nanoTime() - tm1;
count++;
}
result+=c.bitLength();
}
System.out.println((tm / count) + "nsec/mul");
System.out.println(result);
}
}
当我运行它(MacOS 上的 jdk 1.8.0_144-b01)时,它会输出:
When I run this (jdk 1.8.0_144-b01 on MacOS) it outputs:
12089nsec/mul
2559044166
当我在未注释导入行的情况下运行它时:
When I run it with the import line uncommented:
4098nsec/mul
2559044166
使用 JDK 版本的 BigInteger 几乎是我的版本的三倍,即使它使用完全相同的代码.
It's almost three times as fast when using the JDK version of BigInteger versus my version, even if it's using the exact same code.
我已经用 javap 检查了字节码,并比较了使用选项运行时的编译器输出:
I've examined the bytecode with javap, and compared compiler output when running with options:
-Xbatch -XX:-TieredCompilation -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining -XX:CICompilerCount=1
两个版本似乎生成相同的代码.那么热点是否使用了一些我无法在我的代码中使用的预计算优化?我一直都明白他们没有.是什么解释了这种差异?
and both versions seem to generate the same code. So is hotspot using some precomputed optimisations that I can't use in my code? I always understood that they don't. What explains this difference?
推荐答案
是的,HotSpot JVM 有点作弊",因为它有一些你不会使用的特殊版本的 BigInteger
方法在 Java 代码中查找.这些方法称为 JVM 内在函数.
Yes, HotSpot JVM is kind of "cheating", because it has a special version of some BigInteger
methods that you won't find in Java code. These methods are called JVM intrinsics.
特别是,BigInteger.multiplyToLen
是 HotSpot 中的一种内在方法.有一个特殊的 在 JVM 源代码库中的手工编码汇编实现,但仅适用于 x86-64 架构.
In particular, BigInteger.multiplyToLen
is an instrinsic method in HotSpot. There is a special hand-coded assembly implementation in JVM source base, but only for x86-64 architecture.
您可以使用 -XX:-UseMultiplyToLenIntrinsic
选项禁用此内在函数,以强制 JVM 使用纯 Java 实现.在这种情况下,性能将与您复制的代码的性能相似.
You may disable this instrinsic with -XX:-UseMultiplyToLenIntrinsic
option to force JVM to use pure Java implementation. In this case the performance will be similar to the performance of your copied code.
PS这是一个列表的其他HotSpot内在方法.
P.S. Here is a list of other HotSpot intrinsic methods.
相关文章