猿问

Java Parallel Stream 与 ExecutorService 的性能

假设我们有一个列表并且想要选择满足某个属性的所有元素(比如一些函数 f)。有 3 种方法可以并行执行此过程。


一 :


listA.parallelStream.filter(element -> f(element)).collect(Collectors.toList());

二:


listA.parallelStream.collect(Collectors.partitioningBy(element -> f(element))).get(true);

三:


ExecutorService executorService = Executors.newFixedThreadPool(nThreads);

//separate the listA into several batches

for each batch {

     Future<List<T>> result = executorService.submit(() -> {

          // test the elements in this batch and return the validate element list

     });

}

//merge the results from different threads.

假设测试功能是 CPU 密集型任务。我想知道哪种方法更有效。非常感谢。


噜噜哒
浏览 245回答 2
2回答

收到一只叮咚

一和二使用 ForkJoinPool,它专为并行处理一项任务而设计,而 ThreadPoolExecutor 用于并发处理独立任务。所以一和二应该更快。

潇潇雨雨

当您使用 时.filter(element -> f(element)).collect(Collectors.toList()),它会将匹配的元素收集到 a 中List,而.collect(Collectors.partitioningBy(element -> f(element)))将所有元素收集到两个列表中的任何一个中,然后您删除其中一个并仅通过 检索匹配项列表.get(true)。很明显,第二个变体只能在最佳情况下与第一个变体相当,即如果所有元素无论如何都与谓词匹配,或者当 JVM 的优化器能够删除冗余工作时。在最坏的情况下,例如当没有元素匹配时,第二个变体收集所有元素的列表,然后才删除它,而第一个变体不会收集任何元素。第三个变体没有可比性,因为您没有展示实际的实现,而只是一个草图。将假设的实现与实际进行比较是没有意义的。您描述的逻辑与并行流实现的逻辑相同。所以你只是在重新发明轮子。您可能会做一些比参考实现稍微好一点的事情,或者只是更好地针对您的特定任务量身定制,但是您忽略 Stream API 实现者在持续数年的开发过程中已经考虑过的事情的可能性要高得多。所以我不会对你的第三个变体下任何赌注。如果我们增加您完成第三个变体的实现所需的时间,那么它永远不会比仅使用其他变体中的任何一个更有效。所以第一个变体是最有效的变体,特别是因为它也是最简单、最易读、直接表达你的意图的。
随时随地看视频慕课网APP

相关分类

Java
我要回答