当注释掉从未执行过的代码时,Java程序运行得较慢

当注释掉从未执行过的代码时,Java程序运行得较慢,java,recursion,compiler-optimization,Java,Recursion,Compiler Optimization,我在我的一个Java程序中观察到一些奇怪的行为。我试图尽可能地去除代码,同时仍然能够复制行为。下面是完整的代码 public class StrangeBehaviour { static boolean recursionFlag = true; public static void main(String[] args) { long startTime = System.nanoTime(); for (int i = 0; i <

我在我的一个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;
    }
}
公共类行为{
静态布尔递归标志=true;
公共静态void main(字符串[]args){
long startTime=System.nanoTime();
对于(int i=0;i<10000;i++){
功能性(6,0);
}
long-endTime=System.nanoTime();
System.out.format(“%.2f秒已过。\n”,(endTime-startTime)/1000.0/1000/1000);
}
静态布尔函数(int递归深度,int递归开关){
如果(recursionDepth==0){return true;}
返回函数B(递归深度、递归开关);
}
静态布尔函数B(int recursionDepth,int recursionSwitch){
对于(int i=0;i<16;i++){
if(奇怪行为.递归标志){
如果(递归开关==0){
if(function(recursionDepth-1,1-recursionSwitch))返回true;
}否则{
如果(!function(recursionDepth-1,1-recursionSwitch))返回false;
}
}否则{
//这个区块永远不会进入。
//然而,注释掉下面的一行会使程序运行变慢!
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(“…”);
}
}
返回false;
}
}
我有两个函数,
function()
functionB()
递归调用。这两个函数都采用控制递归终止的
recursionDepth
参数
functionA()
调用
functionB()
最多一次,且
recursionDepth
保持不变
functionB()
使用递归深度-1调用
function()
16次。当使用
0
recursionDepth
调用
function()
时,递归终止

functionB()
有一个包含大量
System.out.println()调用的代码块。此块永远不会被输入,因为输入由
布尔递归标志
变量控制,该变量设置为
true
,并且在程序执行期间从未更改。但是,注释掉一个
println()
调用会导致程序运行较慢。在我的机器上,当其中一个调用被注释掉时,执行时间是2秒

是什么导致了这种行为?我唯一的猜测是,与代码块长度(或函数调用数等)相关的参数触发了一些幼稚的编译器优化。如果您对此有进一步的了解,我们将不胜感激


编辑:我正在使用JDK1.8。

注释的代码会影响内联的处理方式。 如果functionB变得更长/更大(字节码指令更多),它将不会内联到functionA中

因此@J3D1能够使用VMOptions手动关闭functionB()的内联:
-XX:CompileCommand=dontinline,com.jd.benchmarking.skangebeh‌​aviour::functionB
这似乎可以用较短的函数消除延迟

使用vm选项,您可以显示内联
-XX:+UnlockDiagnosticVMOptions-XX:+PrintInline

更大的版本,不会内联函数B 较短的版本将尝试内联functionB,导致进一步尝试。
主要是猜测,但较大的/内联字节码将导致分支预测和缓存问题

我是@k5_389;,似乎存在一个阈值来确定是否内联函数。如果JIT编译器决定内联它,它将导致大量的工作和时间,如
-XX:+printcomilation
所示:

  task-id
    158   32       3       so_test.StrangeBehaviour::functionB (326 bytes)   made not entrant
    159   35       3       java.lang.String::<init> (82 bytes)
    160   36  s    1       java.util.Vector::size (5 bytes)
    1878   37 %     3       so_test.StrangeBehaviour::main @ 6 (65 bytes)
    1898   38       3       so_test.StrangeBehaviour::main (65 bytes)
    2665   39       3       java.util.regex.Pattern::has (15 bytes)
    2667   40       3       sun.misc.FDBigInteger::mult (64 bytes)
    2668   41       3       sun.misc.FDBigInteger::<init> (30 bytes)
    2668   42       3       sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
    2.51 seconds elapsed.
新时间和JIT编译器信息:

    140   34       3       so_test.StrangeBehaviour::functionB (326 bytes)   made not entrant
    145   36       3       java.lang.String::<init> (82 bytes)
    148   37  s    1       java.util.Vector::size (5 bytes)
    162   38       4       so_test.StrangeBehaviour::functionA (12 bytes)
    163   33       3       so_test.StrangeBehaviour::functionA (12 bytes)   made not entrant
    1916   39 %     3       so_test.StrangeBehaviour::main @ 6 (65 bytes)
    1936   40       3       so_test.StrangeBehaviour::main (65 bytes)
    2686   41       3       java.util.regex.Pattern::has (15 bytes)
    2689   42       3       sun.misc.FDBigInteger::mult (64 bytes)
    2690   43       3       sun.misc.FDBigInteger::<init> (30 bytes)
    2690   44       3       sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
    2.55 seconds elapsed.
140 34 3 so_测试。奇怪行为::函数B(326字节)未进入
145 36 3 java.lang.String::(82字节)
148 37 s1 java.util.Vector::size(5字节)
162 38 4 so_测试。奇怪行为::函数(12字节)
163 33 3 so_测试。奇怪行为::函数(12字节)未进入
1916 39%3 so_测试。奇怪行为::main@6(65字节)
1936 40 3 so_测试。奇怪行为::main(65字节)
2686 41 3 java.util.regex.Pattern::has(15字节)
2689 42 3 sun.misc.FDB
  task-id
    158   32       3       so_test.StrangeBehaviour::functionB (326 bytes)   made not entrant
    159   35       3       java.lang.String::<init> (82 bytes)
    160   36  s    1       java.util.Vector::size (5 bytes)
    1878   37 %     3       so_test.StrangeBehaviour::main @ 6 (65 bytes)
    1898   38       3       so_test.StrangeBehaviour::main (65 bytes)
    2665   39       3       java.util.regex.Pattern::has (15 bytes)
    2667   40       3       sun.misc.FDBigInteger::mult (64 bytes)
    2668   41       3       sun.misc.FDBigInteger::<init> (30 bytes)
    2668   42       3       sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
    2.51 seconds elapsed.
  task-id
    126   35       4       so_test.StrangeBehaviour::functionA (12 bytes)
    130   33       3       so_test.StrangeBehaviour::functionA (12 bytes)   made not entrant
    131   36  s    1       java.util.Vector::size (5 bytes)
    14078   37 %     3       so_test.StrangeBehaviour::main @ 6 (65 bytes)
    14296   38       3       so_test.StrangeBehaviour::main (65 bytes)
    14296   39 %     4       so_test.StrangeBehaviour::functionB @ 2 (318 bytes)
    14300   40       4       so_test.StrangeBehaviour::functionB (318 bytes)
    14304   34       3       so_test.StrangeBehaviour::functionB (318 bytes)   made not entrant
    14628   41       3       java.util.regex.Pattern::has (15 bytes)
    14631   42       3       sun.misc.FDBigInteger::mult (64 bytes)
    14632   43       3       sun.misc.FDBigInteger::<init> (30 bytes)
    14632   44       3       sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
    14.50 seconds elapsed.
        if (StrangeBehaviour.recursionFlag) {
            int a = 1;
            int b = 1;
            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("...");
        }
    140   34       3       so_test.StrangeBehaviour::functionB (326 bytes)   made not entrant
    145   36       3       java.lang.String::<init> (82 bytes)
    148   37  s    1       java.util.Vector::size (5 bytes)
    162   38       4       so_test.StrangeBehaviour::functionA (12 bytes)
    163   33       3       so_test.StrangeBehaviour::functionA (12 bytes)   made not entrant
    1916   39 %     3       so_test.StrangeBehaviour::main @ 6 (65 bytes)
    1936   40       3       so_test.StrangeBehaviour::main (65 bytes)
    2686   41       3       java.util.regex.Pattern::has (15 bytes)
    2689   42       3       sun.misc.FDBigInteger::mult (64 bytes)
    2690   43       3       sun.misc.FDBigInteger::<init> (30 bytes)
    2690   44       3       sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
    2.55 seconds elapsed.
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);
}