当前位置: 首页 > 知识库问答 >
问题:

Java JIT在运行JDK代码时作弊吗?

刘京
2023-03-14

我正在对一些代码进行基准测试,但我无法使它像使用java.math.biginteger那样快速运行,即使在使用完全相同的算法时也是如此。因此,我将java.math.biginteger源代码复制到自己的包中,并尝试了以下操作:

//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)时,它会输出:

12089nsec/mul
2559044166

当我在未注释导入行的情况下运行它时:

4098nsec/mul
2559044166

使用JDK版本的BigInteger比我的版本快三倍,即使它使用的代码完全相同。

我已经用javap检查了字节码,并比较了使用选项运行时的编译器输出:

-Xbatch -XX:-TieredCompilation -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions 
-XX:+PrintInlining -XX:CICompilerCount=1

而且这两个版本似乎生成了相同的代码。那么hotspot是否使用了一些我不能在代码中使用的预先计算的优化呢?我一直明白他们不是。如何解释这种差异?

共有2个答案

陆伟
2023-03-14

在Java书8中,这的确是一个内在的方法;对该方法稍作修改的版本:

 private static BigInteger test() {

    Random r = new Random(1);
    BigInteger c = null;
    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);
        c = a.multiply(b);
    }
    return c;
}

运行此文件时使用:

 java -XX:+UnlockDiagnosticVMOptions  
      -XX:+PrintInlining 
      -XX:+PrintIntrinsics 
      -XX:CICompilerCount=2 
      -XX:+PrintCompilation   
       <YourClassName>

这将打印很多行,其中一个行是:

 java.math.BigInteger::multiplyToLen (216 bytes)   (intrinsic)

另一方面,在Java 9中,这个方法似乎不再是一个内在的,但反过来,它调用了一个内在的方法:

 @HotSpotIntrinsicCandidate
 private static int[] implMultiplyToLen

所以在Java 9下运行相同的代码(使用相同的参数)会发现:

java.math.BigInteger::implMultiplyToLen (216 bytes)   (intrinsic)

下面是方法的相同代码--只是略有不同的命名。

芮建茗
2023-03-14

是的,HotSpot JVM有点“作弊”,因为它有一些biginteger方法的特殊版本,而这些方法在Java代码中是找不到的。这些方法被称为JVM内含。

特别是biginteger.multipytolen是Hotspot中的固有方法。在JVM源库中有一个特殊的手工编码汇编实现,但仅针对x86-64体系结构。

您可以使用-xx:-useMultiplyTolenintrinsic选项禁用此instrinsic来强制JVM使用纯Java实现。在这种情况下,性能将与复制代码的性能相似。

附注。下面是其他HotSpot内部方法的列表。

 类似资料:
  • 问题内容: 我正在对一些代码进行基准测试,即使使用完全相同的算法,也无法使其运行速度与一样快。因此,我将源代码复制到自己的程序包中并尝试了以下操作: 当我运行此命令(在MacOS上为jdk 1.8.0_144-b01)时,它输出: 当我在未注释导入行的情况下运行它时: 即使使用完全相同的代码,使用BigInteger的JDK版本也要比使用我的版本快三倍。 我已经用javap检查了字节码,并在使用o

  • 问题内容: 大约一年前,我偶然发现了Java的一个不错的功能,我一生都找不到。 通过一些魔术接口,显然可以在运行时声明一些可替换的类或函数。 我找到了一个很好的示例指南,该人运行了一个简单的小程序,该程序打印了一条特定的消息,然后他使用一种我不记得的方法更新了该程序,然后突然该程序将旧的打印功能替换为新的打印功能。 我尝试浏览Java API来激发我的记忆力和谷歌搜索功能,但没有成功。有人可以帮忙

  • 问题内容: codepad.org允许您在线运行C,C ++,D等代码,但不能运行Java …我可以使用Java的网站吗? 问题答案: 还有http://ideone.com/(支持多种语言)

  • 这是我第一次使用OkHttp,我想先研究一些基本的代码。 这是代码,从这里开始: 在Eclipse上运行代码会产生以下输出: 线程“main”java中出现异常。lang.NoClassDefFoundError:okhttp3处的kotlin/TypeCastException。回应书。在okhttp3上创建(ResponseBody.java:210)。内部的Util。(Util.java:6

  • 我收到一个HTTP错误:运行sturts 2应用程序时出现503服务不可用错误。确切的误差是 HTTP错误:503访问/project\u 47/WEB-INF/classes/action/action\u-trial时出现问题。Java语言原因: 由Jetty提供支持:// 我的控制台如下所示: 在端口8080上启动预览服务器 模块:project_47(/project_47)