小心微基准测试的危险!!!
我将代码放入一个方法中并在循环中运行了10次。结果如下:
50, 3,
3, 0,
0, 0,
0, 0,
....
如果循环中没有实际的代码,编译器可以判断出这些循环没有任何用处,并将它们完全优化掉。根据所测得的性能,我怀疑这种优化可能是由javac
完成的。
教训一:编译器通常会优化掉无用的代码。编译器越智能,就越有可能发生这种情况。如果你在编写代码时没有考虑到这一点,那么基准测试可能就毫无意义。
因此,我在两个循环中都添加了以下简单的计算if (i < 2 * j) longK++;
并让测试方法返回longK
的最终值。结果如下:
32267, 33382,
34542, 30136,
12893, 12900,
12897, 12889,
12904, 12891,
12880, 12891,
....
我们明显已经停止编译器将循环优化掉的操作。但现在我们可以在(这种情况下)前两对循环迭代中看到 JVM 预热的效果。第一和第二个循环迭代(一个方法调用)可能完全在解释模式下运行。看起来第三次迭代实际上可能是与 JIT 并行运行。到第三对迭代时,我们很可能正在运行纯本地代码。从那时起,两个版本循环的时间差异只是噪音。
教训2:始终考虑 JVM 预热的影响。这可能会严重扭曲基准测试结果,无论是微观还是宏观。
结论-一旦 JVM 预热完成,两个版本的循环之间没有可测量的差异。