为什么并行流速较慢?

我正在玩无限的流,并制作了这个程序进行基准测试。 基本上,您提供的数字越大,完成的速度就越快。 但是,我惊讶地发现,与顺序流相比,使用parellel流导致性能呈指数级下降。 直觉上,人们会期望在multithreading环境中生成和评估无限的随机数流,但似乎并非如此。 为什么是这样?

final int target = Integer.parseInt(args[0]); if (target  new Double(Math.random()*2147483647).intValue()) //.parallel() .filter(i -> i <= target) .findFirst() .getAsInt() ); endTime = System.currentTimeMillis(); System.out.println("Execution time: "+(endTime-startTime)+" ms"); 

我完全同意其他评论和答案,但实际上,如果目标非常低,您的测试表现得很奇怪。 在我的适度笔记本电脑上,当给出非常低的目标时,并行版本平均慢约60倍。 这种极端的差异无法用流API中的并行化开销来解释,所以我也很惊讶:-)。 IMO的罪魁祸首在于:

 Math.random() 

在内部,此调用依赖于java.util.Random的全局实例。 在Random的文档中写道:

java.util.Random的实例是线程安全的。 但是,跨线程并发使用相同的java.util.Random实例可能会遇到争用,从而导致性能不佳。 请考虑在multithreading设计中使用ThreadLocalRandom。

所以我认为并行执行与顺序执行相比真正糟糕的性能是由随机的线程争用而不是任何其他开销来解释的。 如果您使用ThreadLocalRandom (如文档中所建议的那样),那么性能差异将不那么显着。 另一种选择是实施更高级的数字供应商。

第一次执行工作时,将工作传递给多个线程的成本很高。 这个成本是相当固定的,所以即使你的任务很简单,开销也相对较高。

您遇到的一个问题是,高效低效的代码是确定解决方案执行情况的一种非常糟糕的方法。 此外,它如何运行第一次以及它如何在几秒钟后运行通常可以100倍不同(可以更多)我建议使用一个已经最优的示例,然后才尝试使用多个线程。

例如

 long start = System.nanoTime(); int value = (int) (Math.random() * (target+1L)); long time = System.nanoTime() - value; // don't time IO as it is sooo much slower System.out.println(value); 

注意:在代码已经预热并编译之前,这将不会有效。 即忽略此代码运行的前2-5秒。

根据各种答案的建议,我想我已经修好了。 我不确定确切的瓶颈是什么,但在i5-4590T上,具有以下代码的并行版本比顺序版本执行得更快。 为简洁起见,我只包含(重构)代码的相关部分:

 static IntStream getComputation() { return IntStream .generate(() -> ThreadLocalRandom.current().nextInt(2147483647)); } static void computeSequential(int target) { for (int loop = 0; loop < target; loop++) { final int result = getComputation() .filter(i -> i <= target) .findAny() .getAsInt(); System.out.println(result); } } static void computeParallel(int target) { IntStream.range(0, target) .parallel() .forEach(loop -> { final int result = getComputation() .parallel() .filter(i -> i <= target) .findAny() .getAsInt(); System.out.println(result); }); } 

编辑:我还应该注意,我把它全部放在循环中以获得更长的运行时间。