【发布时间】:2020-02-10 16:37:06
【问题描述】:
更新
在整个 cmets 中,结果证明我采用的基准测试方法不正确,因此结果具有误导性。更正我的方法后(如已接受的答案)结果与预期的一样 - JDK 13 的性能与 JDK 11 一样好。有关更多详细信息,请参阅答案。
原始问题
我在 Windows 10 下使用以下 JMH 测试代码对 HashSet 进行了一些性能基准测试:
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, warmups = 1)
public void init() {
HashSet<String> s = new HashSet<>();
for (int i = 0; i < 1000000; i++) {
s.add(Math.random() + "");
}
s.size();
}
我在不同的 JDK 版本下编译运行,结果如下:
我也用不同的堆大小测试了它(因此每个 JDK 有 3 种不同的颜色)。 JDK 14 当然是今天的预发布快照——只是为了看看 ZGC 在 Windows 下的表现。
我想知道 - JDK 11 之后发生了什么? (注意,对于 JDK 12,它已经开始增长,即使它没有出现在上面的图表中)
【问题讨论】:
-
如果使用
Integer.toString(i)而不是Math.random() + "",结果是否相似?我很好奇回归是由于生成随机数还是HashSet本身造成的。 -
等待...您的
init没有返回任何内容?你也没有Blackhole::consume?我想知道如果你也删除warmups会发生什么。在我看来,如果你只用C2编译器运行它,它们都应该接近于零,因为该方法可以被视为 NOOP -
@Eugene 不,因为
Math.random()将推进全局可见Random种子的状态。基本上,这段代码是在对Math.random()的效率进行基准测试... -
在 JDK 11 和 13 之间的 G1 GC 中发生了 大量 的变化。不确定应该归咎于哪一个,但差异显然是由垃圾收集引起的 - 你可以通过添加
-prof gcJMH 选项来看到这一点,或者只是切换到并行 GC,它在两个 JDK 版本中的性能几乎相同。请注意,您的基准测试不衡量 HashSet 的性能。根据 async-profiler,大约 50% 的 CPU 时间花在 GC 上,大约 25% 将 double 转换为 String。 -
@Eugene 在基准测试中错误地使用随机数是一个常见的错误,尽管在这种特定情况下,它可以避免基准测试被死代码消除。在 Java 7 的某些版本中,
HashMap的唯一创建因更新全局随机种子(可怕的 alt 散列功能)而受到影响。
标签: java garbage-collection jvm java-11 java-13