【发布时间】: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