为什么返回Java对象引用比返回原始类型慢得多

6 浏览
0 Comments

为什么返回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

0