0

我有一个小程序,它是一个相当无意义的简单数字运算练习,让我陷入了循环。

该程序产生了一堆执行简单数学运算的工作线程。最近我改变了一种工人变体的内部循环:

do
{           
    int3 = int1 + int2;
    int3 = int1 * int2;             
    int1++;
    int2++;
    i++;
}
while (i < 128);

类似于:

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];

数组由值不高于 1025 的随机整数填充,并且数组值不会改变。

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

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

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

没有向我的所有代码添加除法操作(超出一定数量的外部循环迭代,这实际上并不是一个修复),有什么方法可以让 JVM 停止将我的代码减少到明显的 NOOP?我已经尝试了几种解决该问题的方法,例如每次外循环迭代生成新的随机值、返回具有增量的简单整数变量以及其他一些废话,但这些解决方案都没有产生理想的结果。要么它继续忽略一系列指令,要么修改对性能的影响非常糟糕,以至于我的除法重变体实际上比没有除法操作的代码执行得更好。

编辑:提供一些上下文:

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

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

tempint4/tempint5:这些是对在程序的主类文件中定义的一对整数数组的引用(Mathtester。是的,我知道这很缺乏想象力)。当程序第一次启动时,有一个短的 do/while 循环,用 1-1025 的随机整数填充每个数组。数组大小为 128 个整数。每个数组都是静态的,尽管引用变量不是。事实上,我没有特别的理由使用参考变量。它们是我尝试进行数组引用交换时的剩余部分,因此,在外循环的每次迭代之后,tempint4 和 tempint5 将被引用到相反的数组。我希望 JVM 不再忽略我的代码块。对于支持除法的代码版本,这似乎有效(有点),因为它从根本上改变了要计算的值。

编辑:使 tempint4 和 tempint5 (因为它们只是参考变量,我实际上指的是主数组,Mathtester.int4 和 Mathtester.int5) volatile 在没有显着降低 CPU 活动量或级别或 CPU 温度的情况下工作。它确实使代码变慢了一点,但这可能表明 JVM 的 NOOPing 比我所知道的要多。

4

2 回答 2

1

在处理 Java 性能时,您必须牢记的第一件事是:

“单行 Java 代码在孤立的情况下根本没有任何意义”。

现代 JVM 是非常复杂的野兽,并且会进行各种优化。如果您尝试测量一小段代码,那么您可能不会测量您认为自己是什么——如果没有非常非常详细地了解 JVM 正在做什么,要正确地做到这一点真的很复杂。

在这种情况下,是的,JVM 很可能正在优化循环。没有简单的方法可以阻止它这样做,而且几乎所有技术都是脆弱的并且是特定于 JVM 版本的(因为一直在开发新的和更聪明的优化并添加到 JVM)。

所以,让我把问题转过来:“你到底想在这里实现什么?为什么要阻止 JVM 优化?”

于 2014-12-28T15:43:46.543 回答
1

有什么方法可以让 JVM 停止将我的代码减少到明显的 NOOP?

是的,通过制作int3 volatile.

于 2014-12-26T14:50:32.110 回答