Javadocs本身并没有回答这个问题,所以它被送到邮件列表上寻找基本原理。我浏览了 lambda-libs-spec-observers 存档,发现了一个专门关于 Collection.parallelStream() 的线程,另一个线程涉及 java.util.Arrays 是否应该提供 parallelStream() 来匹配(或者实际上,是否应该删除它)。没有一劳永逸的结论,所以也许我从另一个清单中遗漏了一些东西,或者这个问题在私下讨论中得到了解决。(也许这次讨论的主要负责人之一布莱恩·戈茨(Brian Goetz)可以填补任何遗漏的东西。Collection.(parallelS|s)tream()
Stream
参与者很好地表达了他们的观点,所以这个答案主要只是相关引文的组织,在[括号]中进行了一些澄清,按重要性顺序呈现(正如我所解释的那样)。
parallelStream() 涵盖了一个非常常见的情况
Brian Goetz 在第一个线程中解释了为什么即使在其他并行流工厂方法被删除后,也有足够的价值来保留:Collections.parallelStream()
我们没有这些[流工厂]的显式并行版本;我们最初这样做了,为了修剪API表面积,我们削减了它们,理论上从API中删除20多种方法值得在表面怪异和性能成本之间进行权衡。但是我们没有在Collection上做出这样的选择。.intRange(...).parallel()
我们可以删除 ,也可以添加所有生成器的并行版本,或者我们可以什么都不做,保持原样。我认为在API设计的基础上,所有这些都是合理的。Collection.parallelStream()
我有点喜欢现状,尽管它不一致。我们没有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() 性能更高
布莱恩·戈茨:
直接版本 [parallelStream()] 的性能更高,因为它需要较少的包装(要将流转换为并行流,您必须首先创建顺序流,然后将其状态的所有权转移到新的流中。
在回应Kevin Bourrillion对效果是否显着的怀疑时,Brian再次指出:
取决于你算了多少。Doug 在并行操作的途中计算了单个对象的创建和虚拟调用,因为在你开始分叉之前,你就站在了阿姆达尔定律的错误一边——这都是在你分叉任何工作之前发生的“串行分数”,这会把你的盈亏平衡阈值进一步推开。因此,快速获得并行操作的设置路径很有价值。
Doug Lea跟进,但对冲了他的立场:
处理并行库支持的人需要对这些事情进行一些态度调整。在即将成为典型机器的机器上,您浪费在设置并行性时浪费的每个周期的成本都是64个周期。如果需要创建 64 个对象才能启动并行计算,您可能会有不同的反应。
也就是说,我总是完全支持为了更好的API而迫使实现者更加努力地工作,只要API不排除有效的实现。因此,如果杀戮真的很重要,我们会找到一些方法来变成一个有点翻转或类似的东西。parallelStream
stream().parallel()
事实上,后面的讨论注意到了较低的Stream.parallel()成本。Arrays.parallelStream()
stream().parallel() stateful 使未来复杂化
在讨论时,将流从顺序切换到并行和向后切换可以与其他流操作交错。Brian Goetz代表Doug Lea解释了为什么顺序/并行模式切换可能会使Java平台的未来发展复杂化:
我将尽我最大的努力来解释为什么:因为它(就像你不喜欢的有状态方法(排序,不同,限制)一样,使我们越来越远离能够以传统的数据并行结构来表达流管道,这进一步限制了我们直接将它们映射到明天的计算基板的能力,无论是矢量处理器, FPGA,GPU或我们烹饪的任何东西。
滤波-映射-减少映射非常清晰地映射到各种并行计算基板;filter-parallel-map-sequential-sorted-limit-parallel-map-uniq-reduce 不会。
因此,这里的整个API设计体现了许多紧张关系,两者之间是使用户可能想要表达的内容变得容易,而这样做是以一种我们可以预测的方式,通过透明的成本模型快速完成。
经过进一步讨论后,此模式切换已被删除。在当前版本的库中,流管道是顺序的或并行的。最后一次调用 /获胜。除了回避有状态性问题之外,此更改还提高了使用从顺序流工厂设置并行管道的性能。sequential()
parallel()
parallel()
将parallelStream()公开为一等公民,可以提高程序员对库的看法,从而使他们能够编写更好的代码
Brian Goetz再次回应了Tim Peierls的论点,即允许程序员在并行之前按顺序理解流:Stream.parallel()
我对这种顺序直觉的价值有一个稍微不同的观点 - 我认为普遍的“顺序期望”是整个努力的最大挑战之一;人们不断带来他们不正确的顺序偏差,这导致他们做一些愚蠢的事情,比如使用单元素数组作为“欺骗”“愚蠢”编译器的一种方式,让他们捕获可变的局部,或者使用lambda作为参数来映射将在计算期间使用的突变状态(以非线程安全的方式), 然后,当它指出他们正在做的事情时,耸耸肩并说“是的,但我没有并行不做。
我们在合并顺序流和并行流时进行了许多设计权衡。我相信,结果是一个干净的,并且会增加图书馆在10年多年后仍然有用的机会,但我特别不喜欢鼓励人们认为这是一个顺序库的想法,侧面钉着一些平行的袋子。