我在我的一个Java程序中观察到一些奇怪的行为。我已经尝试尽可能地精简代码,同时仍然能够复制行为。下面是完整的代码。
public class StrangeBehaviour {
static boolean recursionFlag = true;
public static void main(String[] args) {
long startTime = System.nanoTime();
for (int i = 0; i < 10000; i ++) {
functionA(6, 0);
}
long endTime = System.nanoTime();
System.out.format("%.2f seconds elapsed.\n", (endTime - startTime) / 1000.0 / 1000 / 1000);
}
static boolean functionA(int recursionDepth, int recursionSwitch) {
if (recursionDepth == 0) { return true; }
return functionB(recursionDepth, recursionSwitch);
}
static boolean functionB(int recursionDepth, int recursionSwitch) {
for (int i = 0; i < 16; i++) {
if (StrangeBehaviour.recursionFlag) {
if (recursionSwitch == 0) {
if (functionA(recursionDepth - 1, 1 - recursionSwitch)) return true;
} else {
if (!functionA(recursionDepth - 1, 1 - recursionSwitch)) return false;
}
} else {
// This block is never entered into.
// Yet commenting out one of the lines below makes the program run slower!
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
}
}
return false;
}
}
我有两个函数,functionA()
和functionB()
,它们以递归方式相互调用。这两个函数都有一个控制递归终止的recursionDepth
参数。functionA()
在recursionDepth
不变的情况下最多调用functionB()
一次。functionB()
使用recursionDepth - 1
调用functionA()
16次。当使用0
的recursionDepth
调用functionA()
时,递归终止。
functionB()
有一个包含多个System.out.println()
调用的代码块。此块从不进入,因为条目由设置为true
的boolean recursionFlag
变量控制,并且在程序执行期间不会更改。但是,注释掉其中的一个println()
调用都会导致程序运行速度变慢。在我的机器上,在所有println()
调用都存在的情况下,执行时间<0.2秒,当其中一个调用被注释掉时,执行时间>2秒。
是什么导致了这种行为?我唯一的猜测是,有一些天真的编译器优化是由一个与代码块长度(或函数调用次数等)相关的参数触发的。如果您对此有进一步的了解,我们将不胜感激!
编辑:我使用的是JDK 1.8。
发布于 2017-01-02 14:09:26
完整的答案是k5_和托尼的答案的组合。
OP发布的代码省略了在执行基准测试之前触发HotSpot编译的预热循环;因此,当包含打印语句时,(在我的计算机上)速度提高了10倍,将HotSpot中用于将字节码编译为CPU指令的时间以及CPU指令的实际运行时间结合在一起。
如果我在计时循环之前添加一个单独的预热循环,那么使用print语句只会有2.5倍的加速。
这表明当方法是内联的时候,HotSpot/JIT编译需要更长的时间(正如Tony解释的那样),并且代码的运行需要更长的时间,这可能是因为更差的缓存或分支预测/流水线性能,正如k5_所显示的那样。
public static void main(String[] args) {
// Added the following warmup loop before the timing loop
for (int i = 0; i < 50000; i++) {
functionA(6, 0);
}
long startTime = System.nanoTime();
for (int i = 0; i < 50000; i++) {
functionA(6, 0);
}
long endTime = System.nanoTime();
System.out.format("%.2f seconds elapsed.\n", (endTime - startTime) / 1000.0 / 1000 / 1000);
}
https://stackoverflow.com/questions/41410743
复制相似问题