Java Stream.toList()的性能会比collector.toList()更好吗
JDK正在引入一个API。下面是一个基准代码,我试图将其性能与现有的Java Stream.toList()的性能会比collector.toList()更好吗,java,performance,java-stream,jmh,java-16,Java,Performance,Java Stream,Jmh,Java 16,JDK正在引入一个API。下面是一个基准代码,我试图将其性能与现有的收集器进行比较。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 CollectorsVsStreamT
收集器进行比较。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();
}
}
当然,领域专家的第一个问题是,基准测试程序是否正确?测试类是在MacOS上执行的。如需更多详细信息,请务必告知我
跟进,据我从读数推断,
流的平均时间、吞吐量和采样时间。toList
看起来比收集器好。toList
。这种理解正确吗?Stream::toList
构建在toArray
之上,而不是collect
。在toArray
中有许多优化,使其可能比收集更快,尽管这在很大程度上取决于细节。如果流管道(从源到最终中间操作)的大小为大小
,则可以预先确定目标数组的大小(而不是像toList
收集器必须做的那样进行潜在的重新分配)。如果管道进一步被子化
,则并行执行不仅可以预先确定结果数组的大小,但是可以计算精确的每碎片偏移量,这样每个子任务都可以将其结果精确地放在正确的位置,从而无需将中间结果复制到最终结果中
因此,根据细节,
toList
可能比collect
快得多。在某些情况下,Stream::toList
很可能更有效,但这实际上取决于细节Stream::toList
建立在toArray
的基础上,对于具有大小(对于并行流,理想情况下是补贴)特征的源,toArray
经过优化,以减少与collect
相比的重新分配和复制。但是,如果您试图测量两个收集器之间的差异,您应该尽量减少流管线其余部分的工作量。我会将源代码生成和装箱从基准方法中移出,进入一个在基准方法之外初始化的@状态
变量,类似于Stream.of(data).toList()
。拳击确实在扭曲你的数据。我还将包括并行运行。@Holger默认实现中的额外步骤是在发生这种情况时强制进行防御复制。toArray
违反了其规范并保留了对返回数组的引用。如果没有防御性副本,则可以修改从默认的toList
实现返回的列表。@StuartMarkstoArray
是流API的一部分,因此如果实现违反规范并返回共享数组,toArray
方法的调用方可能已经中断。为什么toList()
方法的调用方比toArray()的调用方获得更多的保证?这很简单:如果实现尊重规范,那么这些方法按照规范所说的去做。修复一个可能损坏的实现不是默认实现的任务。防御副本适用于基本类,如String
,但不适用于包装器,因为包装器无论如何都不能保证是真正不可变的集合。@StuartMarks好的,这不是合适的地方。最后一点意见是,在很多地方,JDK代码的结果取决于未知接口实现的正确性,而在这里使用这种不信任/昂贵的防御代码看起来是任意的。而且,在文档中,这显然是不必要的。即使对于未经大小调整的流,内部使用的spined缓冲区也可能比填充ArrayList
之类的Collectors.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