当有可能时,我应该总是使用并行流吗?

3 浏览
0 Comments

当有可能时,我应该总是使用并行流吗?

通过Java 8和lambda,我们可以很容易地将集合作为流进行迭代,并且同样容易地使用并行流。以下是两个示例,第二个示例使用parallelStream(来自文档):

myShapesCollection.stream()
    .filter(e -> e.getColor() == Color.RED)
    .forEach(e -> System.out.println(e.getName()));
myShapesCollection.parallelStream() // <-- 这个使用并行流
    .filter(e -> e.getColor() == Color.RED)
    .forEach(e -> System.out.println(e.getName()));

只要我不关心顺序,使用并行流总是有益的吗?人们可能认为将工作分配到更多核心上会更快。

还有其他考虑因素吗?什么时候应该使用并行流,什么时候应该使用非并行流?

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

0
0 Comments

在这个问题中,原因是因为在并行化之前需要考虑4个因素。解决方法是使用一个相对简单的公式来确定并行加速的机会。

在这个问题中,出现的原因是在进行并行化之前需要考虑一些因素。Brian Goetz在演示中详细解释了以下4个要点:

1. 分割/分解成本:有时分割成本比完成工作更昂贵!

2. 任务调度/管理成本:在将工作交给另一个线程的时间内可以完成大量工作。

3. 结果组合成本:有时组合涉及复制大量数据。例如,添加数字是廉价的,而合并集合是昂贵的。

4. 局部性:这是一个重要的问题,每个人可能都会忽略。您应该考虑高速缓存缺失,如果CPU因为高速缓存缺失而等待数据,那么并行化是没有任何好处的。这就是为什么基于数组的源最适合并行化的原因,因为接下来的索引(接近当前索引)被缓存,CPU遇到高速缓存缺失的机会较少。

他还提到了一个相对简单的公式来确定并行加速的机会。

NQ模型:

N x Q > 10000

其中,

N = 数据项的数量

Q = 每个项的工作量

“每个项的工作量”是以什么单位衡量的?10000代表什么?

0
0 Comments

在使用Stream API时,可以很容易地将计算的方式抽象出来,使得在顺序和并行之间切换变得容易。然而,仅仅因为容易,并不意味着随意地使用.parallel()就是一个好主意。事实上,随意使用.parallel()是一个不好的主意。

首先,需要注意的是,并行计算除了在更多核心可用时可能提供更快的执行速度之外,没有其他的好处。并行执行总是涉及更多的工作量,因为除了解决问题之外,还必须执行子任务的分派和协调。希望通过将工作分配到多个处理器上来更快地得到答案;这是否真的发生取决于很多因素,包括数据集的大小,对每个元素进行的计算量,计算的性质(特别是一个元素的处理是否与其他元素的处理有交互?),可用的处理器数量以及竞争这些处理器的其他任务的数量。

此外,需要注意的是,并行计算通常也会暴露出顺序实现中隐藏的非确定性;有时这并不重要,或者可以通过约束所涉及的操作(即,约简操作符必须是无状态和可结合的)来减轻这种影响。

实际上,有时并行计算会加速计算,有时不会,有时甚至会减慢计算速度。最好的方法是首先使用顺序执行进行开发,然后在以下情况下应用并行性:

(A) 你知道增加性能真的有好处,并且

(B) 它确实会提供增加的性能。

(A) 是一个业务问题,而不是技术问题。如果你是一个性能专家,通常可以通过查看代码来确定(B),但明智的做法是进行测量。(并且,在你相信(A)之前,甚至不要费心;如果代码足够快,最好将你的思维周期应用在其他地方。)

并行性的最简单的性能模型是"NQ"模型,其中N是元素的数量,Q是每个元素的计算量。一般来说,你需要使NQ的乘积超过一定的阈值,才能获得性能上的好处。对于一个低Q问题,比如"将数字从1加到N",通常在N=1000和N=10000之间会看到一个平衡点。对于更高Q的问题,你会在更低的阈值上看到平衡点。

但现实情况非常复杂。因此,在成为专家之前,首先确定顺序处理实际上是否对你产生了一些成本,并且然后测量并行性是否有帮助。

这篇文章提供了关于NQ模型的更多细节:gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html

我认为你需要详细说明关于非确定性的论断 - 据我所知,除非问题本身发生了变化,确定性问题不可能变成非确定性问题 - 这也适用于流,它们只是在可迭代集合的段上包装线程;因此,除非元素本身以写入方式相互交互,否则整个流不可能是非确定性的 --- 如果你的集合从一开始就是非确定性的,即使最好的for/while循环也无法改变这个事实。

:我不确定我是否理解了你的问题 - 你是希望有人解释一下为什么多个并行线程会使计算变得非确定性吗?

...不是的吗?并行线程本身并不会使任何东西变得非确定性。适当的算法可能会这样做,但仅限于此 - 技术本身与其使用/应用的结果无关,完全是无稽之谈 - 很容易编写不会改变确定性的并行代码。

:将流从顺序切换到并行确实会改变算法(大多数情况下)。这里提到的确定性是指您的(任意的)操作符可能依赖的属性(Stream实现无法知道这一点),但当然不应该依赖。这就是这个答案中提到的那一部分所要表达的。如果您关心规则,您可以获得确定性的结果,就像您所说的那样(否则并行流将是相当无用的),但也有有意允许非确定性的可能性,比如使用findAny而不是findFirst...

"首先,需要注意的是,并行计算除了在更多核心可用时可能提供更快的执行速度之外,没有其他的好处" - 或者如果应用的操作涉及IO(例如myListOfURLs.stream().map((url) -> downloadPage(url))...)。

既然你是语言设计者,你必须注意:只要算法在语义上允许,就应该始终使用并行性。因为即使在NQ低于10k时,顺序也更快,差异也很小,无论哪种方式都没有关系。但是,如果NQ超过10k,并且远远超过10k,那么并行性是唯一的选择。

确实,这还有一个优点是语义的正确性。应该将可并行化的代码标记为可并行化,并且将其交给编译器来判断是否应该实际上并行执行。这正是选择ifswitch的相同论点线:只需使用语义正确的那个,并将其交给编译器来决定如何最佳地进行实现

这是一个不错的理论,但可悲的是天真的(参考30年来自动并行化编译器构建的历史)。由于不可能在足够的时间内猜测正确的时间,以避免在我们不可避免地犯错时使用户感到恼火,负责任的做法是让用户说出他们想要的。对于大多数情况,默认值(顺序)是正确的,并且更可预测。

:不要将并行流用于IO。它们仅用于CPU密集型操作。并行流使用ForkJoinPool.commonPool(),你不希望阻塞任务进入那里。

在你写的NQ模型中,"Q是每个元素的计算量"是什么意思,它表示计算一个元素所花费的时间?我如何计算Q?

0
0 Comments

在使用并行流时,是否应该始终使用并行流?这个问题的出现原因是,并行流与顺序流相比有更高的开销。协调线程需要花费大量时间。我通常会默认使用顺序流,只有在以下情况下才考虑使用并行流:

- 我有大量要处理的项目(或每个项目的处理需要时间且可以并行化)

- 我已经有性能问题了

- 我没有在多线程环境中运行进程(例如:在Web容器中,如果我已经有许多并行处理的请求,添加额外的并行处理层可能会产生更多的负面效果)

在你的例子中,性能将由对System.out.println()的同步访问驱动,将此过程并行化将没有效果,甚至会产生负面效果。

此外,记住,并行流并不能神奇地解决所有同步问题。如果共享资源被谓词和函数使用,则必须确保一切都是线程安全的。特别是,副作用是你确实需要担心的事情,如果你选择并行处理。

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

好答案。我想补充一点,如果你有大量要处理的项目,只会增加线程协调问题;只有当每个项目的处理需要时间且可以并行化时,才可能有用并行化。

我不同意。Fork/Join系统只会将N个项目分成4个部分,然后按顺序处理这4个部分。然后将这4个结果合并。如果“大量”确实非常大,即使是对于快速单元处理,并行化也可能是有效的。但是一如既往,你必须进行测量。

我有一个实现了Runnable接口的对象集合,我调用start()将它们用作线程,将其更改为在.forEach()中并行使用java 8流可以吗?然后我可以将线程代码从类中删除。但是有没有什么缺点?

如果4个部分按顺序处理,那么并行处理和顺序处理没有区别,对吗?请澄清一下

他显然是指每个4个部分的元素将按顺序处理。然而,各个部分可以同时处理。换句话说,如果有几个CPU核心可用,每个部分可以在自己的核心上独立于其他部分运行,同时按顺序处理自己的元素。(注意:我不知道并行Java流的工作原理,我只是想澄清JBNizet的意思。)

“大量”是多少?一如既往,测量是关键,但是通常应该如何分类为“大量”?

我认为最后一点应该加粗 无论如何,要进行测量,不要猜测!只有测量才能告诉你并行性是否值得。 并行读取列表将推动Java生成线程来读取列表。在处理不生成线程的进程时,并行性会有所帮助。例如,在Spring管理的Bean中运行任务,在Bean内部,Spring保留了一个线程用于处理任务。每种处理场景都不同,如果不调整和测量哪种处理方式可以改善处理效果,那么比较是不完整的。

完美的答案!这是在代码中添加并行流之前要检查的清单。

如果与filter谓词匹配的值很少,即使有println也可能有所帮助。

谢谢,在我这种情况下(处理16k或60k个条目的列表),速度更慢 🙂

0