Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/379.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Oracle JDK8:JVM将展开的循环转换为NOOP?_Java_Jvm_Loop Unrolling - Fatal编程技术网

Java Oracle JDK8:JVM将展开的循环转换为NOOP?

Java Oracle JDK8:JVM将展开的循环转换为NOOP?,java,jvm,loop-unrolling,Java,Jvm,Loop Unrolling,我有一个小程序,在简单的数字运算中是一个毫无意义的练习,这让我陷入了一个循环 该程序生成了一组执行简单数学运算的工作线程。最近,我将worker的一个变体的内部循环更改为: do { int3 = int1 + int2; int3 = int1 * int2; int1++; int2++; i++; } while (i < 128); 数组由值不高于1025的随机整数填充,数组值不变 最终的结果

我有一个小程序,在简单的数字运算中是一个毫无意义的练习,这让我陷入了一个循环

该程序生成了一组执行简单数学运算的工作线程。最近,我将worker的一个变体的内部循环更改为:

do
{           
    int3 = int1 + int2;
    int3 = int1 * int2;             
    int1++;
    int2++;
    i++;
}
while (i < 128);
数组由值不高于1025的随机整数填充,数组值不变

最终的结果是程序运行得更快,尽管仔细检查似乎表明CPU在运行新版本的代码时实际上没有做任何事情。JVM似乎已经发现,它可以安全地忽略在外循环一次迭代后替换内循环的代码,因为它只是在同一组数据上反复进行相同的计算

为了说明我的观点,旧代码可能需要大约27000毫秒才能运行,并显著提高了CPU的工作温度(它还显示了所有内核的100%利用率)。新代码运行可能需要5毫秒(有时更少),并且不会导致CPU利用率或温度出现峰值。增加外循环迭代次数不会改变新代码的行为,即使迭代次数增加了100倍或更多

我有另一个版本的worker,与上面的版本相同,只是它有一个除法运算以及加法和乘法运算。在其新的展开形式中,支持分区的版本也比以前的版本快得多,但实际上需要一点时间(第一次运行约300毫秒,后续运行约200毫秒,尽管预热有点奇怪),并且在短暂的运行中产生了CPU温度的显著峰值。在运行程序时,增加外循环迭代次数似乎会导致温度现象在经过一定时间后基本停止,尽管所有内核的利用率仍为100%。我的猜测是JVM在处理除法操作时,需要花费更长的时间才能确定哪些操作可以安全地忽略,而且它并没有忽略所有操作

除了向我的所有代码中添加除法操作(除了一定数量的外循环迭代之外,这实际上不是一个修复),还有什么方法可以让JVM停止将代码缩减到明显的NOOPs?我已经尝试了几个解决方案,比如每次外循环迭代生成新的随机值,返回到简单的整数变量并进行递增,以及其他一些无稽之谈,但是这些解决方案都没有产生理想的结果。要么它继续忽略这一系列指令,要么修改带来的性能影响太糟糕,以至于我的重除法变体实际上比没有除法操作的代码性能更好

编辑:提供一些上下文:

i:该变量是一个整数,用于do/while循环中的循环计数器。它在包含工作代码的类文件中定义。它的初始值是0。在较新版本的worker中不再使用它

int1/int2:这些是在包含工作代码的类文件中定义的整数。它们的初始值都是0。它们在旧版本的代码中用于为内部循环的每次迭代提供不断变化的值。我所要做的就是在每个循环迭代中向上递增一次,JVM将被迫忠实地执行每个操作。不幸的是,这个循环显然阻止了SIMD的使用。每次外部循环迭代时,int1和int2的值都会被重置,以防止int1、int2或int3溢出(我发现整数溢出会不必要地降低代码的速度,就像允许浮点达到无穷大一样)

tempint4/tempint5:这些是对程序主类文件中定义的一对整数数组的引用(Mathtester.Yes,unimaginative,我知道)。当程序第一次启动时,会有一个短的do/while循环,用随机整数填充每个数组,随机整数的排列范围为1-1025。数组的大小为128个整数。每个数组都是静态的,尽管引用变量不是静态的。事实上,我没有特别的理由使用参考变量。它们是我尝试进行数组引用交换时留下的,这样,在外部循环的每次迭代之后,tempint4和tempint5都会被引用到相反的数组。我希望JVM不再忽略我的代码块。对于启用除法的代码版本,这似乎起了作用(某种程度上),因为它从根本上改变了要计算的值。将tempint4替换为tempint5,反之亦然,不会改变加法和乘法运算的结果,因此JVM仍然可以忽略这些结果

编辑:使tempint4和tempint5(因为它们只是引用变量,我实际上指的是主数组Mathtester.int4和Mathtester.int5)易失性工作,而不会显著降低CPU活动量、级别或CPU温度。它确实使代码慢了一点,但这很可能表明JVM的运行速度比我所知道的要快

有没有办法让JVM停止将代码缩减到明显的NOOPs


是的,通过将
int3
设置为volatile
处理Java性能时,您必须牢记的第一件事是:

“一行Java代码在隔离状态下意味着什么也没有”

现代JVM非常复杂,可以进行各种优化。如果您试图度量一些小的代码片段,那么很可能无法度量您认为自己是什么——如果不非常详细地了解JVM正在做什么,那么要正确地进行度量是非常复杂的

在这种情况下,是的,JVM很可能正在优化循环。有
int3 = tempint4[0] + tempint5[0];
int3 = tempint4[0] * tempint5[0];

int3 = tempint4[1] + tempint5[1];
int3 = tempint4[1] * tempint5[1];

int3 = tempint4[2] + tempint5[2];
int3 = tempint4[2] * tempint5[2];

int3 = tempint4[3] + tempint5[3];
int3 = tempint4[3] * tempint5[3];

...

int3 = tempint4[127] + tempint5[127];
int3 = tempint4[127] * tempint5[127];