【发布时间】:2021-04-20 19:37:30
【问题描述】:
JDK 正在引入 API Stream.toList() 和 JDK-8180352。这是我尝试将其性能与现有Collectors.toList 进行比较的基准代码:
@BenchmarkMode(Mode.All)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 20, time = 1, batchSize = 10000)
@Measurement(iterations = 20, time = 1, batchSize = 10000)
public class CollectorsVsStreamToList {
@Benchmark
public List<Integer> viaCollectors() {
return IntStream.range(1, 1000).boxed().collect(Collectors.toList());
}
@Benchmark
public List<Integer> viaStream() {
return IntStream.range(1, 1000).boxed().toList();
}
}
结果汇总如下:
Benchmark Mode Cnt Score Error Units
CollectorsVsStreamToList.viaCollectors thrpt 20 17.321 ± 0.583 ops/s
CollectorsVsStreamToList.viaStream thrpt 20 23.879 ± 1.682 ops/s
CollectorsVsStreamToList.viaCollectors avgt 20 0.057 ± 0.002 s/op
CollectorsVsStreamToList.viaStream avgt 20 0.040 ± 0.001 s/op
CollectorsVsStreamToList.viaCollectors sample 380 0.054 ± 0.001 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.00 sample 0.051 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.50 sample 0.054 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.90 sample 0.058 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.95 sample 0.058 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.99 sample 0.062 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.999 sample 0.068 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.9999 sample 0.068 s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p1.00 sample 0.068 s/op
CollectorsVsStreamToList.viaStream sample 525 0.039 ± 0.001 s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.00 sample 0.037 s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.50 sample 0.038 s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.90 sample 0.040 s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.95 sample 0.042 s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.99 sample 0.050 s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.999 sample 0.051 s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.9999 sample 0.051 s/op
CollectorsVsStreamToList.viaStream:viaStream·p1.00 sample 0.051 s/op
CollectorsVsStreamToList.viaCollectors ss 20 0.060 ± 0.007 s/op
CollectorsVsStreamToList.viaStream ss 20 0.043 ± 0.006 s/op
当然,领域专家的第一个问题是基准测试程序是否正确?测试类在 MacOS 上执行。如果需要任何进一步的详细信息,请告诉我。
跟进,据我从读数推断,Stream.toList 的平均时间、吞吐量和采样时间看起来比Collectors.toList 更好。这种理解正确吗?
【问题讨论】:
-
Stream::toList在某些情况下很可能更有效——但这实际上取决于细节。Stream::toList建立在toArray的基础上,对于具有 SIZED(最好是 SUBSIZED,用于并行流)特征的源,与collect相比,toArray经过优化以减少重新分配和复制。 -
但是,如果您尝试测量两个收集器之间的差异,则应尽量减少流管道其余部分的工作。我会将源代码生成和装箱移出基准方法并放入在基准方法之外初始化的
@State变量中,例如Stream.of(data).toList()。拳击肯定会扭曲您的数据。我还包括并行运行。 -
@Holger 默认实现中的额外步骤是在
this.toArray违反其规范并保留对返回数组的引用时强制执行防御性副本。如果没有防御性副本,则可以修改从默认toList实现返回的列表。 -
@StuartMarks
toArray是 Stream API 的一部分,因此如果实现违反规范并返回共享数组,toArray方法的调用者可能已经中断。为什么toList()方法的调用者要比toArray()的调用者获得更多的保证?这很简单:如果实现尊重规范,这些方法就会按照规范所说的去做。修复可能损坏的实现不是默认实现的任务。防御性副本适用于像String这样的基本类,但不适用于永远不能保证是真正不可变集合的包装器。 -
@StuartMarks 好的,这不是地方。仅最后一条评论,有很多地方,JDK 代码的结果取决于接口的未知实现的正确性,并且在这个地方有这样的不信任/昂贵的防御性编码看起来很随意。而且,在文档中,这绝对是不必要的。
标签: java performance java-stream jmh java-16