Nic*_*ini 5 java performance jit
我试图了解当代码被执行多次时,我可以在 Java 中实现什么样的编译时优化。我对以下场景中的算术简化特别感兴趣:
想象一下,您需要使用相同的 3d 仿射变换来变换一百万个 3d 点。如果转换结果是纯翻译,那么一个好的优化器将能够将 12 次乘法和 12 次加法仅转换为 3 次加法,因为所有乘法都是乘以 1,而许多加法都是带零的加法。
在尝试这个“复杂场景”之前,我只是在循环中运行了一些乘法和加法,尽管我一直在阅读有关 Java 的 JIT 编译器的很酷的内容,但我还是有点失望。
首先 - 什么有效:执行许多乘以 1 似乎被简化并且执行得非常快:
tic();
final int nRepetitions = 100_000_000;
final double factor1 = 1.0d;
value = 0.0d;
for (int i=0;i<nRepetitions;i++) {
for (int j=0;j<20;j++) {
value = value * factor1;
}
}
System.out.println("Result = "+value);
toc();
Run Code Online (Sandbox Code Playgroud)
我得到这个执行速度,很快:
Result with graalvm-ce-17\bin\java.exe
----------------------
Repeating 100000000 multiplication by factor1 = 1.0, a final variable
The code is put in the main method
This is overall is No op, and should be super fast
Result = 0.0
Elapsed time 64.8528 ms
----------------------
Run Code Online (Sandbox Code Playgroud)
如果我执行相同的计算,但通过调用函数,我根本没有得到任何优化,执行速度约为 2 秒。
Result with graalvm-ce-17\bin\java.exe
----------------------
Repeating 100000000 multiplication by factor1 = 1.0, a final variable
The code is put in the main method
This is overall is No op, and should be super fast
Result = 0.0
Elapsed time 64.8528 ms
----------------------
Run Code Online (Sandbox Code Playgroud)
其功能为:
public static void repeatMultiply(int nRepetitions, double value, final double multFactor) {
for (int i=0;i<nRepetitions;i++) {
for (int j=0;j<20;j++) {
value = value * multFactor;
}
}
System.out.println("Result = "+value);
}
Run Code Online (Sandbox Code Playgroud)
现在代码运行速度非常慢:
----------------------
Repeating 100000000 multiplication by factor1 = 1.0d
Function called = repeatMultiply
This is overall is No op, and should be super fast
Result = 0.0
Elapsed time 1815.1354 ms
Run Code Online (Sandbox Code Playgroud)
我测试过其他东西。factor1不像第一个示例中那样声明变量final会破坏我看到的唯一优化。然后,我尝试加零而不是乘以一,但情况更糟:我总是得到“长”执行时间,在我的机器上大约为两秒。
我测试过 Oracle JDK v1.8 和 v18,还有 Graal VM Community Edition 17,但似乎没有什么区别。我已经在https://gist.github.com/NicoKiaru/2949e6969087e75b07b21596d80c7882中执行了所有代码和测试的要点
我希望您能启发我,让我知道这些结果是否反映了 Java JIT 编译器的内在限制,即我在测试中是否做错了什么。
有什么方法可以通过 JIT 编译来实现我的目标(仿射变换计算的自动优化),或者我应该停止梦想并在 Java 代码中显式测试“简单场景”(如翻译)?
编辑
计算常量 JEP https://openjdk.org/jeps/8312611是否有可能允许此类优化,或者完全不相关?
小智 0
JIT 编译器对于进行可能改变代码行为的优化持谨慎态度。尽管从数学角度来看,加零或乘以一似乎是不必要的,但这些操作可能出于特定原因(例如代码可读性或处理边缘情况)而明确包含在代码中。删除它们可能会改变程序的语义。