当前位置: 首页 > 知识库问答 >
问题:

为什么collection.ParallelStream()在.Stream().Parallel()做同样的事情时还存在?

通京
2023-03-14

在Java8中,使用两个返回stream 的方法扩展了集合接口:stream()(返回顺序流)和ParallelStream()(返回可能并行的流)。Stream本身也有一个parallel()方法,该方法返回一个等效的并行流(或者将当前流改为并行流,或者创建一个新流)。

重复有明显的缺点:

如果现有实现具有一个具有不兼容返回类型的类似名称的方法,则向接口添加方法可能会发生冲突。在stream()的基础上添加parallelStream()会使风险增加一倍,但收益很小。(请注意,parallelStream()曾一度被命名为parallear(),但我不知道它被重命名是为了避免名称冲突还是出于其他原因。)

为什么collection.ParallelStream()在调用collection.Stream()时会存在。Parallel()做同样的事情?

共有1个答案

轩辕佑运
2023-03-14

collection.(parallelSs)tream()stream本身的Javadocs并不能回答这个问题,所以它只能在邮件列表中找到基本原理。我浏览了lambda-libs-spec-observers档案,发现了一个专门关于collection.parallelStream()的线程,另一个线程涉及java.util.Arrays是否应该提供parallelStream()来匹配(或者实际上,是否应该删除它)。没有一劳永逸的结论,所以也许我从另一个列表中遗漏了什么,或者这件事在私下讨论中解决了。(或许这次讨论的主要负责人之一布莱恩·戈茨可以填补任何缺失的内容。)

与会者很好地阐述了他们的观点,所以这个答案基本上只是一个相关引用的组织,[括号]中有一些澄清,按重要性(我的解释)排列。

Brian Goetz在第一个线程中解释了为什么collections.parallelStream()在删除了其他并行流工厂方法之后仍然具有足够的价值:

我们没有这些[流工厂]的明确并行版本;我们最初是这样做的,为了减少API的表面积,我们根据这样的理论来削减它们,即从API中删除20+个方法是值得的,因为它的表面积和.intrange(...).parallel()的性能代价是值得的。但我们没有在收集时做出这样的选择。

我们可以删除collection.ParallelStream(),或者添加所有生成器的并行版本,或者什么也不做,保持原样。我认为在API设计的基础上,所有这些都是合理的。

我有点喜欢现状,尽管它不一致。我们没有2N个流构造方法,而是有N+1个--但是这个额外的1覆盖了大量的情况,因为它被每个集合继承。所以我可以自圆其说为什么有一个额外的方法是值得的,为什么接受不进一步的不一致是可以接受的。

别人不同意吗?N+1[Collections.ParallelStream()only]是这里的实际选择吗?还是应该去追求N的纯净[依靠Stream.Parallel()]?还是2N“全厂并行版”的便捷性和一致性?或者有一些甚至更好的n+3[Collections.ParallelStream()加上其他特殊情况],对于一些我们想要给予特殊支持的其他特别选择的情况?

Brian Goetz在后面关于数组的讨论中坚持这一立场。ParallelStream()

我还是很喜欢Collection.ParallelStream;它具有巨大的可发现性优势,并且在API表面积上提供了相当大的回报--这又是一种方法,但在很多地方都提供了价值,因为收集将是流源的一种真正常见的情况。

布莱恩·戈茨:

直接版本[ParallelStream()]的性能更好,因为它需要更少的包装(要将流转换为并行流,您必须首先创建顺序流,然后将其状态的所有权转移到新流中。)

针对Kevin Bourrillion对效果是否显著的怀疑,Brian再次:

处理并行库支持的人需要对这类事情进行一些态度调整。在一个即将成为典型的机器上,设置并行性所浪费的每一个周期都要花费64个周期。如果需要创建64个对象来启动并行计算,您可能会有不同的反应。

也就是说,我总是完全支持强迫实现者为了更好的API而更加努力地工作,只要API不排除高效的实现。因此,如果删除ParallelStream真的很重要,那么我们将找到一些方法将Stream().Parallel()转换为位翻转或其他。

实际上,后面关于arrays.parallelStream()的讨论注意到了较低的stream.parallel()开销。

我将尽力解释原因:因为它(就像您也不喜欢的有状态方法(sort、Distinctive、limit)),使我们越来越远地不能用传统的数据并行构造来表达流管道,这进一步限制了我们将它们直接映射到未来计算基底的能力,不管是向量处理器、FPGA、GPU还是我们制造的任何东西。

Filter-map-reduce Map[s]非常干净地还原到各种并行计算基板;filter-parallel-map-sequential-sorted-limit-parallel-map-uniq-reduce不是。

因此,这里的整个API设计体现了许多紧张关系,既要使用户可能想要表达的东西更容易表达,又要以一种我们可以预测的方式,用透明的成本模型快速表达。

在进一步讨论后,该模式切换被移除。在库的当前版本中,流管道要么是顺序的,要么是并行的;对序列()/并行()的最后一次调用获胜。除了避免了状态性问题之外,这一更改还提高了使用parallel()从顺序流工厂设置并行管道的性能。

Brian Goetz再次回应Tim Peierls的论点,即stream.parallel()允许程序员在并行之前按顺序理解流:

对于这种顺序直觉的价值,我有一个略微不同的观点--我认为普遍存在的“顺序期望”是整个努力中最大的挑战之一;人们不断地带来他们错误的顺序偏见,这导致他们做一些愚蠢的事情,比如使用一个元素数组来“欺骗”“愚蠢的”编译器,让它捕获一个可变的局部,或者使用lambdas作为参数来映射将在计算过程中使用的变异状态(以非线程安全的方式),然后,当它指出他们正在做的事情时,对它不屑一顾,说“是的,但我不是并行地做它。”

为了合并顺序流和并行流,我们做了大量的设计权衡。结果,我相信,是一个干净的,将增加图书馆在10年以上仍然有用的机会,但我不特别喜欢鼓励人们认为这是一个顺序图书馆,一些平行袋钉在边上的想法。

 类似资料:
  • 问题内容: 在Java 8中,Collection接口扩展了两个方法,它们返回return :返回顺序流;和,返回可能并行的流。Stream本身也具有一种返回等效并行流的方法(将当前流更改为并行或创建新流)。 复制有明显的缺点: 令人困惑。提出一个问题,假设parallelStream()可能返回顺序流,是否必须同时调用parallelStream()。parallel()以确保流是并行的。如果不

  • output指示在1s暂停之前执行16个流元素,然后再执行16个元素,依此类推。因此,即使forkjoinpool是用100个线程创建的,也只有16个线程被使用。 当我使用超过23个线程时,就会出现这种模式:

  • ChatGPT能做什么?ChatGPT可以做的21件事情

  • 问题内容: 在Java 8中,我们有类,它奇怪地有一个方法 因此,您可能希望它实现接口,而该接口正是需要此方法,但事实并非如此。 当我想使用foreach循环遍历Stream时,我必须做类似的事情 我在这里想念什么吗? 问题答案: 人们已经在邮件列表 asked 上问过同样的问题。主要原因是Iterable也具有可重复的语义,而Stream没有。 我认为主要原因是Iterable暗示可重用性,而S

  • 我遇到的问题是代码始终无法通过“0004”,因为它卡在wait_for上,而wait_for需要附加的位置参数:'event',从我在discord.py站点的示例中看到的示例中,它应该是旁边括号中的'message'。 ''' discord.client.wait_for('message',check=检查) 错误:Discord.Client没有属性“wait_for” 对于discord

  • 问题内容: 当我使用pip时,没有sudo通常不会起作用。我经常看到人们在不使用sudo的情况下使用pip,所以我在做什么错呢? 我读到不建议使用sudo安装pip软件包。我知道可以不用sudo使用pip,但是要安装,我必须先使用sudo。 当我尝试不使用sudo安装pip时,我得到: 尝试通过以下方式安装flask时: 问题答案: 在Unix / Linux系统中用于在另一个用户中使用 其 权限