为什么返回Java对象引用比返回原始类型慢得多
为什么返回Java对象引用比返回原始类型慢得多
我们正在开发一个对延迟敏感的应用程序,并且一直在使用jmh进行各种方法的微基准测试。在微基准测试了一个查找方法并对结果感到满意后,我实现了最终版本,结果发现最终版本比我刚刚进行基准测试时慢了3倍。
罪魁祸首是实现的方法返回了一个枚举对象而不是一个整数。下面是简化版的基准测试代码:
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class ReturnEnumObjectVersusPrimitiveBenchmark {
enum Category {
CATEGORY1,
CATEGORY2,
}
@Param( {"3", "2", "1" })
String value;
int param;
@Setup
public void setUp() {
param = Integer.parseInt(value);
}
@Benchmark
public int benchmarkReturnOrdinal() {
if (param < 2) {
return Category.CATEGORY1.ordinal();
}
return Category.CATEGORY2.ordinal();
}
@Benchmark
public Category benchmarkReturnReference() {
if (param < 2) {
return Category.CATEGORY1;
}
return Category.CATEGORY2;
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(ReturnEnumObjectVersusPrimitiveBenchmark.class.getName()).warmupIterations(5)
.measurementIterations(4).forks(1).build();
new Runner(opt).run();
}
}
以上是基准测试的结果:
# VM invoker: C:\Program Files\Java\jdk1.7.0_40\jre\bin\java.exe
# VM options: -Dfile.encoding=UTF-8
Benchmark (value) Mode Samples Score Error Units
benchmarkReturnOrdinal 3 thrpt 4 1059.898 ± 71.749 ops/us
benchmarkReturnOrdinal 2 thrpt 4 1051.122 ± 61.238 ops/us
benchmarkReturnOrdinal 1 thrpt 4 1064.067 ± 90.057 ops/us
benchmarkReturnReference 3 thrpt 4 353.197 ± 25.946 ops/us
benchmarkReturnReference 2 thrpt 4 350.902 ± 19.487 ops/us
benchmarkReturnReference 1 thrpt 4 339.578 ± 144.093 ops/us
仅仅改变了函数的返回类型,性能就差了将近3倍。我认为返回枚举对象与返回整数的唯一区别是一个返回64位值(引用),另一个返回32位值。我的一个同事猜测返回枚举对象会增加额外的开销,因为需要跟踪潜在GC的引用。(但鉴于枚举对象是静态的最终引用,它似乎不需要这样做)。
对于性能差异的解释是什么?
更新:
我在这里分享了一个maven项目,任何人都可以克隆它并运行基准测试。如果有时间/兴趣,看看其他人是否能够复现相同的结果将会很有帮助。(我在两台不同的机器上复现了这个问题,分别是Windows 64位和Linux 64位,都使用了Oracle Java 1.7 JVM)。@ZhekaKozlov说他没有看到任何方法之间的差异。
运行命令:(克隆仓库后)
mvn clean install
java -jar .\target\microbenchmarks.jar function.ReturnEnumObjectVersusPrimitiveBenchmark -i 5 -wi 5 -f 1