在可能的情况下,我应该始终使用并行流吗?

使用 Java 8 和 lambdas,可以很容易地将集合作为流进行迭代,并且同样容易使用并行流。文档中的两个示例,第二个示例使用 parallelStream:

myShapesCollection.stream()
    .filter(e -> e.getColor() == Color.RED)
    .forEach(e -> System.out.println(e.getName()));

myShapesCollection.parallelStream() // <-- This one uses parallel
    .filter(e -> e.getColor() == Color.RED)
    .forEach(e -> System.out.println(e.getName()));

只要我不关心顺序,使用并行总是有益的吗?有人会认为它更快地将工作划分到更多内核上。

还有其他注意事项吗?何时应使用并行流,何时应使用非并行流?

(提出这个问题是为了引发关于如何以及何时使用并行流的讨论,而不是因为我认为始终使用它们是一个好主意。


答案 1

与顺序流相比,并行流的开销要高得多。协调线程需要花费大量时间。默认情况下,我会使用顺序流,并且只考虑并行流,如果

  • 我有大量项目要处理(或者每个项目的处理都需要时间并且是可并行化的)

  • 我首先遇到了性能问题

  • 我还没有在多线程环境中运行该进程(例如:在Web容器中,如果我已经有许多请求要并行处理,则在每个请求中添加额外的并行性层可能会产生更多的负面影响而不是积极影响)

在您的示例中,性能无论如何都将由 同步访问 驱动,并且使此过程并行不会产生任何影响,甚至是负面影响。System.out.println()

此外,请记住,并行流并不能神奇地解决所有同步问题。如果进程中使用的谓词和函数使用共享资源,则必须确保所有内容都是线程安全的。特别是,副作用是你真正需要担心的事情,如果你并行。

无论如何,测量,不要猜测!只有测量才能告诉您并行度是否值得。


答案 2

Stream API 旨在使编写计算变得容易,其方式是从执行方式中抽象出来的,从而在顺序和并行之间轻松切换。

然而,仅仅因为它很容易,并不意味着它总是一个好主意,事实上,仅仅因为你可以,就到处掉下来是一个主意。.parallel()

首先,请注意,并行性除了在有更多内核可用时可以更快地执行之外,没有其他好处。并行执行总是比顺序执行涉及更多的工作,因为除了解决问题之外,它还必须执行子任务的调度和协调。希望您能够通过分解多个处理器的工作来更快地获得答案。这是否确实发生取决于很多事情,包括数据集的大小,您对每个元素执行的计算量,计算的性质(具体而言,一个元素的处理是否与其他元素的处理相互作用?),可用处理器的数量以及竞争这些处理器的其他任务的数量。

此外,请注意,并行性还经常暴露计算中的非确定性,而这些非确定性通常被顺序实现所隐藏;有时这并不重要,或者可以通过约束所涉及的操作来缓解(即,约简运算符必须是无状态和关联的)。

实际上,有时并行性会加快计算速度,有时不会,有时甚至会减慢计算速度。最好首先使用顺序执行进行开发,然后应用并行性,其中

(A)您知道提高性能和实际好处

(B)它实际上将提供更高的性能。

(A)是业务问题,不是技术问题。如果您是性能专家,您通常能够查看代码并确定(B),但明智的方法是进行测量。(而且,在你确信(A)之前,甚至不要打扰;如果代码足够快,最好将你的大脑周期应用到其他地方。

并行性最简单的性能模型是“NQ”模型,其中是元素的数量,并且是每个元素的计算。通常,您需要产品 NQ 超过某个阈值,然后才能开始获得性能优势。对于像“将数字从 加起来”这样的低 Q 问题,您通常会看到 和 之间的盈亏平衡。对于高Q值问题,您将在较低的阈值下看到盈亏平衡。NQ1NN=1000N=10000

但现实相当复杂。因此,在你成为专家之前,首先要确定顺序处理何时真正使你付出了代价,然后衡量并行性是否有帮助。


推荐