Collection.(parallelS|s)tream() 和 Stream 的 Javadocs 本身并没有回答这个问题,所以它在邮件列表中找到了理由。我浏览了 lambda-libs-spec-observers 档案,发现 one thread specifically about Collection.parallelStream() 和另一个线程涉及 java.util.Arrays should provide parallelStream() 是否匹配(或者实际上,是否应该删除它)。没有一劳永逸的结论,所以也许我错过了另一个列表中的某些内容,或者此事已在私下讨论中解决。 (也许Brian Goetz,本次讨论的负责人之一,可以填补任何缺失的内容。)
参与者的观点很好,所以这个答案主要只是对相关引用的组织,在[括号]中进行了一些澄清,按重要性顺序排列(按照我的解释) .
parallelStream() 涵盖了一个非常常见的情况
Brian Goetz 在第一个线程中,解释了为什么 Collections.parallelStream() 足够有价值,即使在其他并行流工厂方法被删除后也可以保留:
我们确实没有每个[流工厂]都有明确的并行版本;我们做了
最初,为了修剪 API 表面积,我们在
从 API 中删除 20 多种方法的理论值得权衡
.intRange(...).parallel() 的表面恶心和性能成本。
但是我们没有在 Collection 中做出这样的选择。
我们可以删除Collection.parallelStream(),也可以添加
所有生成器的并行版本,否则我们无能为力
保持原样。我认为所有的 API 设计都是合理的。
我有点喜欢现状,尽管它自相矛盾。代替
有 2N 种流构造方法,我们有 N+1——但那额外的 1
涵盖了大量的案例,因为它被每个人继承
收藏。所以我可以向自己证明为什么要使用额外的 1 方法
是值得的,为什么接受不进一步的不一致是
可以接受。
其他人不同意吗? N+1 [Collections.parallelStream() only] 是这里的实际选择吗?或者我们应该去
为了N 的纯度[依赖Stream.parallel()]?还是2N[所有工厂的并行版本]的便利性和一致性?或者是
还有一些更好的 N+3 [Collections.parallelStream() 加上其他特殊情况],对于其他一些特别选择的情况,我们
要特别支持吗?
Brian Goetz在后面关于Arrays.parallelStream()的讨论中支持这个立场:
我还是很喜欢 Collection.parallelStream;它有巨大的
可发现性优势,并提供相当大的 API 回报
表面积——另一种方法,但在很多地方提供价值,
因为 Collection 将是流源的一个非常常见的情况。
parallelStream() 性能更高
Brian Goetz:
直接版本 [parallelStream()] 性能更高,因为它需要更少的包装(到
将流转换为并行流,您必须首先创建
顺序流,然后将其状态的所有权转移到一个新的
流。)
针对 Kevin Bourrillion 对效果是否显着的怀疑,Brian again:
取决于您计算的认真程度。道格计算单个物体
并行操作途中的创建和虚拟调用,
因为在你开始分叉之前,你站在 Amdahl 的错误一边
法则——这是在你可以分叉之前发生的所有“串行分数”
任何工作,这将您的盈亏平衡门槛推得更远。所以得到
快速并行操作的设置路径很有价值。
Doug Lea follows up,但对冲他的位置:
处理并行库支持的人需要一些态度
调整这些事情。在即将成为典型的机器上,
您浪费的每个周期设置并行性都会花费您说的 64 个周期。
如果需要 64,你可能会有不同的反应
创建对象以启动并行计算。
也就是说,我始终完全支持强制实施者
为了更好的 API 而努力工作,只要
API 不排除有效的实施。所以如果杀
parallelStream 真的很重要,我们会想办法
将stream().parallel() 转换为位翻转之类的。
确实,后面关于Arrays.parallelStream()takes notice of lower Stream.parallel() cost的讨论。
stream().parallel() 状态使未来复杂化
在讨论时,将流从顺序切换到并行并返回可能与其他流操作交错。 Brian Goetz, on behalf of Doug Lea,解释了为什么顺序/并行模式切换可能会使 Java 平台的未来开发变得复杂:
我会尽力解释原因:因为它(就像有状态的
您也不喜欢的方法(排序,不同,限制)),请移步我们
越来越远无法表达流管道
传统数据并行结构的术语,这进一步限制了
我们将它们直接映射到明天的计算基板的能力,
无论是矢量处理器、FPGA、GPU 还是我们制作的任何东西。
Filter-map-reduce map[s] 非常干净地适用于各种并行计算
基材; filter-parallel-map-sequential-sorted-limit-parallel-map-uniq-reduce
没有。
因此,这里的整个 API 设计体现了制作之间的许多紧张关系
易于表达用户可能想要表达的事情,并且正在做
以一种我们可以预见的方式以透明的成本快速实现
模型。
此模式切换为removed after further discussion。在当前版本的库中,流管道是顺序的或并行的;最后一次致电sequential()/parallel() 获胜。除了回避状态问题之外,此更改还提高了使用 parallel() 从顺序流工厂设置并行管道的性能。
将parallelStream() 公开为一等公民可以提高程序员对库的认识,从而使他们编写出更好的代码
Brian Goetz again,作为对Tim Peierls's argument 的回应,Stream.parallel() 允许程序员在并行之前按顺序理解流:
我对这个顺序的价值有一点不同的看法
直觉——我认为普遍的“顺序期望”是一个如果
整个工作的最大挑战;人们一直
带来他们不正确的顺序偏差,这导致他们做傻事
诸如使用单元素数组作为“欺骗”“愚蠢”的一种方式
编译器让他们捕获一个可变的本地,或使用 lambdas
映射将在
计算(以非线程安全的方式),然后,当它指出
他们在做什么,耸耸肩说“是的,但我没有做
它是并行的。”
我们在设计上做了很多权衡来合并顺序和并行
流。我相信,结果是一个干净的结果,并将增加
图书馆在 10 多年内仍然有用的机会,但我没有
特别喜欢鼓励人们认为这是一个
顺序库,侧面钉有一些平行袋。