【问题标题】:Performance of behaviorally identical conditional checks [duplicate]行为相同的条件检查的执行[重复]
【发布时间】:2023-03-31 05:53:01
【问题描述】:

我回答了this question,并在运行代码时发现了一些让我感兴趣的东西。部分问题是关于各种样式的相同条件检查的性能,例如:

if (x > 0) { 
    if (y > 0) { 
        if (z > 0) { 
            if (complexCondition()) { 
                 noOp();
            }
        }
    }
}

if (x > 0 && y > 0 && z > 0 && complexCondition()) { 
     noOp();
}

我写了一个程序,计算执行一定次数的迭代需要多长时间,结果是我不明白的。

这是我的程序在我的机器上的输出(Win 8.1 64 位,javac 1.8.0_11(Oracle),java 1.0.0_11(Java SE,Oracle)。我已经多次运行每个试验,它们产生可重复的结果结果。

x = 9, y = 2, z = 8, countTrials = 1, iterationsPerTrial = 2736000
Method        Average Runtime(ns)
guardIf()     5746173
multipleIf()  4868180
chainedIf()   9316172

x = 9, y = 2, z = 8, countTrials = 100, iterationsPerTrial = 2736000
guardIf()     1642750
multipleIf()  1121897
chainedIf()   1739522

x = -1, y = 2, z = 8, countTrials = 1, iterationsPerTrial = 2736000 // I expect shorter results with x = -1, because they all short curcuit
guardIf()     9245313
multipleIf()  8728608
chainedIf()   11718332

x = -1, y = 2, z = 8, countTrials = 100, iterationsPerTrial = 2736000 
guardIf()     1754279
multipleIf()  1611278
chainedIf()   4947295

毫秒测量(使用org.apache.commons.lang3.time.StopWatch 实现)

x = 9, y = 2, z = 8, countTrials = 1, iterationsPerTrial = Integer.MAX_VALUE
Method        Average Runtime(ms)
guardIf()     1664
multipleIf()  1095
chainedIf()   1654

x = -1, y = 2, z = 8, countTrials = 1, iterationsPerTrial = Integer.MAX_VALUE
Method        Average Runtime(ms)
guardIf()     4886
multipleIf()  4926
chainedIf()   4862

x = 9, y = 2, z = 8, countTrials = 10, iterationsPerTrial = Integer.MAX_VALUE
Method        Average Runtime(ms)
guardIf()     1673
multipleIf()  1108
chainedIf()   1682

x = -1, y = 2, z = 8, countTrials = 10, iterationsPerTrial = Integer.MAX_VALUE
Method        Average Runtime(ms)
guardIf()     4364
multipleIf()  4363
chainedIf()   4877

为什么所有方法的运行都需要更长的时间(在 chainIf 的情况下,几乎要多 300% 的时间),而当它们检查的值允许它们全部短路时?

我已经用调试器逐步完成了所有这些,而且它们确实很短(正如我所期望的那样)。我检查了字节码,chainedIf()multipleIf() 完全相同。我很困惑和好奇。

我不确定我执行测量的方式是否存在缺陷,所以我在下面包含了我的程序。

程序源

class TrialResult {
    public long GuardIf = 0; 
    public long MultipleIf = 0; 
    public long ChainedIf = 0; 

    public TrialResult(long guardIf, long multipleIf, long chainedIf) { 
        this.GuardIf = guardIf; 
        this.MultipleIf = multipleIf; 
        this.ChainedIf = chainedIf; 
    }
}

public class Program {

    private int x; 
    private int y; 
    private int z; 

    public static void main(String[] args) { 
        Program program = new Program(); 
        List<TrialResult> trials = new ArrayList<TrialResult>(); 
        int countTrials = 1; 

        for (int j = 0; j < countTrials; j++) {     
            long t0 = 0, t1 = 0; 

            t0 = System.nanoTime();
            for (long i = 0; i < 2073600; i++) { 
                program.chainedIf();
            }
            t1 = System.nanoTime();
            long chainIf = t1 - t0;

            t0 = System.nanoTime();
            for (long i = 0; i < 2073600; i++) { 
                program.multipleIf();
            }

            t1 = System.nanoTime();
            long multipleIf = t1 - t0;

            t0 = System.nanoTime();
            for (long i = 0; i < 2073600; i++) { 
                program.guardIf();
            }
            t1 = System.nanoTime();
            long guardIf = t1 - t0;
            System.out.printf("Trial %d completed\r\n", j+1);

            trials.add(new TrialResult(guardIf, multipleIf, chainIf)); 
        }

        long chainIf = 0, multipleIf = 0, guardIf = 0; 
        for (TrialResult r : trials) { 
            chainIf += r.ChainedIf; 
            multipleIf += r.MultipleIf; 
            guardIf += r.GuardIf; 
        }
        System.out.printf("%d, %d, %d", guardIf / trials.size(), multipleIf / trials.size(), chainIf / trials.size()); 
    }

    private Program() {
        x = 9; 
        y = 2; 
        z = 8; 
    }

    private void chainedIf() { 
        if (x > 0) { 
            if (y > 0) { 
                if (z > 0) { 
                    if (complexCondition()) { 
                         noOp();
                    }
                }
            }
        }
    }

    private void multipleIf() { 
        if (x > 0 && y > 0 && z > 0 && complexCondition()) { 
             noOp();
        }
    }

    public void guardIf() { 
        if (x <= -1) { 
            return; 
        }

        if (y <= -1) { 
            return; 
        }

        if (z <= -1) { 
            return; 
        }

        if (!complexCondition()) { 
            return; 
        }

         noOp();
    }

    private boolean complexCondition() { 
        return (x > 0 && 
            y < x && 
            y + z > x
        );      
    }

    private void noOp() { 
        return; 
    }
}

【问题讨论】:

  • 微基准几乎完全没有意义,因为有 JIT,而且你的时钟精度不是纳秒精度。如果字节码相同,那么 JIT 是我能想到的 only 答案。
  • 除了JIT,这个微基准可能会受到分支预测的影响。
  • 我认为System.nanoTime() 的文档是错误的,即“例如,测量某些代码执行所需的时间:”。我将重新审视我的基准。

标签: java performance jit


【解决方案1】:

当所有方法检查的值允许它们全部缩短时,为什么所有方法的运行需要更长的时间(在 chainIf 的情况下,几乎要多 300% 的时间)?

因为您的基准测试有问题。请阅读类似问题的答案:
Java loop gets slower after some runs / JIT's fault?

【讨论】:

    猜你喜欢
    • 2021-06-20
    • 1970-01-01
    • 2013-10-04
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 2014-10-20
    • 2022-01-18
    • 2019-07-08
    相关资源
    最近更新 更多