好的,只有一个适当的JMH测试才能证明某个方法的快慢。当然,前提是如果您真的想知道为什么数字是这样的,您也应该了解底层机器代码。我将把这留给您,并在此测试中仅向您展示一些细节。
最初的回答:
package com.so;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
@Warmup(iterations = 5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
public class IfElseCompare {
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(IfElseCompare.class.getName())
.jvmArgs("-ea")
.build();
new Runner(opt).run();
}
private int resolveValueMultipleIfs(IfElseExecutionPlan plan) {
int x = -1;
if (plan.value() == 0) {
x = 0;
}
if (plan.value() == 1) {
x = 1;
}
if (plan.value() == 2) {
x = 2;
}
assert x != -1;
return x;
}
private int resolveValueIfElse(IfElseExecutionPlan plan) {
int x = -1;
if (plan.value() == 0) {
x = 0;
} else if (plan.value() == 1) {
x = 1;
} else if (plan.value() == 2) {
x = 2;
}
assert x != -1;
return x;
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
public int multipleIf(IfElseExecutionPlan plan) {
return resolveValueMultipleIfs(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
public int ifElse(IfElseExecutionPlan plan) {
return resolveValueIfElse(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-Xint")
public int multipleIfsfNoJIT(IfElseExecutionPlan plan) {
return resolveValueMultipleIfs(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-Xint")
public int ifElseNoJIT(IfElseExecutionPlan plan) {
return resolveValueIfElse(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:-TieredCompilation")
public int multipleIfsC2Only(IfElseExecutionPlan plan) {
return resolveValueMultipleIfs(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:-TieredCompilation")
public int ifElseC2Only(IfElseExecutionPlan plan) {
return resolveValueIfElse(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:TieredStopAtLevel=1")
public int multipleIfsC1Only(IfElseExecutionPlan plan) {
return resolveValueMultipleIfs(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:TieredStopAtLevel=1")
public int ifElseC1Only(IfElseExecutionPlan plan) {
return resolveValueIfElse(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1,
jvmArgsAppend = {
"-XX:+UnlockExperimentalVMOptions",
"-XX:+EagerJVMCI",
"-Dgraal.ShowConfiguration=info",
"-XX:+UseJVMCICompiler",
"-XX:+EnableJVMCI"
})
public int multipleIfsGraalVM(IfElseExecutionPlan plan) {
return resolveValueMultipleIfs(plan);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1,
jvmArgsAppend = {
"-XX:+UnlockExperimentalVMOptions",
"-XX:+EagerJVMCI",
"-Dgraal.ShowConfiguration=info",
"-XX:+UseJVMCICompiler",
"-XX:+EnableJVMCI"
})
public int ifElseGraalVM(IfElseExecutionPlan plan) {
return resolveValueIfElse(plan);
}
}
最初的回答:
以下是结果:
IfElseCompare.ifElse avgt 2 2.826 ns/op
IfElseCompare.multipleIf avgt 2 3.061 ns/op
IfElseCompare.ifElseC1Only avgt 2 3.927 ns/op
IfElseCompare.multipleIfsC1Only avgt 2 4.397 ns/op
IfElseCompare.ifElseC2Only avgt 2 2.507 ns/op
IfElseCompare.multipleIfsC2Only avgt 2 2.428 ns/op
IfElseCompare.ifElseGraalVM avgt 2 2.587 ns/op
IfElseCompare.multipleIfsGraalVM avgt 2 2.854 ns/op
IfElseCompare.ifElseNoJIT avgt 2 232.418 ns/op
IfElseCompare.multipleIfsfNoJIT avgt 2 303.371 ns/op
如果您反编译带有多个
if
条件的版本:最初的回答。
0x000000010cf8542c: test %esi,%esi
0x000000010cf8542e: je 0x000000010cf8544f ;*ifne {reexecute=0 rethrow=0 return_oop=0}
; - com.so.IfElseCompare::resolveValueMultipleIfs@3 (line 21)
0x000000010cf85430: cmp $0x1,%esi
0x000000010cf85433: je 0x000000010cf8545e ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0}
; - com.so.IfElseCompare::resolveValueMultipleIfs@10 (line 25)
0x000000010cf85435: cmp $0x2,%esi
0x000000010cf85438: je 0x000000010cf8546e ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0}
; - com.so.IfElseCompare::resolveValueMultipleIfs@17 (line 29)
一系列的cmp/je
- 比较并跳转,这是非常可预期的。
if/else
的反编译代码也是相同的(我会让你自己反编译并亲眼看到);使用(java-12)生成的汇编代码:
Original Answer翻译成"最初的回答"
java -XX:+UnlockDiagnosticVMOptions
-XX:CICompilerCount=2
-XX:-TieredCompilation
"-XX:CompileCommand=print,com/so/IfElseCompare.resolveValueMultipleIfs"
com.so.IfElseCompare