注意:我已经在另一个SO帖子中解决了这个问题 - 在嵌套的Java 8并行流动作中使用信号量可能是DEADLOCK.这是一个错误吗? - 但是这篇文章的标题表明问题与使用信号量有关 - 这有点分散了讨论的注意力.我正在创建这个,以强调嵌套循环可能有性能问题 - 虽然这两个问题可能是一个共同的原因(也许是因为我花了很多时间来弄清楚这个问题).(我不认为它是重复的,因为它强调另一种症状 - 但如果你只是删除它).
问题:如果嵌套两个Java 8 stream.parallel().forEach循环并且所有任务都是独立的,无状态的等等 - 除了提交到公共FJ池 - 然后在并行循环内嵌套并行循环执行得更差而不是在并行循环内嵌套顺序循环.更糟糕的是:如果同步包含内循环的操作,您将获得DEADLOCK.
演示性能问题
如果没有"同步",您仍然可以观察到性能问题.您可以在以下网址找到演示代码:http://svn.finmath.net/finmath%20experiments/trunk/src/net/finmath/experiments/concurrency/NestedParallelForEachTest.java (有关更详细的说明,请参阅JavaDoc).
我们的设置如下:我们有一个嵌套的stream.parallel().forEach().
内环是独立的(无状态,无干扰等 - 除了使用公共池之外)并且在最坏的情况下总共消耗1秒,即如果处理顺序.
外循环的一半任务在该循环之前消耗10秒.
在该循环之后,一半消耗10秒.
因此,每个线程总共消耗11秒(最坏情况).*我们有一个布尔值,允许将内部循环从parallel()切换到sequential().
现在:将24个外循环任务提交给具有并行性的池8我们期望24/8*11 =最多33秒(在8核或更好的机器上).
结果是:
内部顺序循环:33秒.
内部并行循环:> 80秒(我有92秒).
问题:你能证实这种行为吗?这是人们对框架的期望吗?(我现在更加小心,声称这是一个错误,但我个人认为这是由于ForkJoinTask的实现中的一个错误.备注:我已将此发布到并发兴趣(请参阅http:// cs.oswego.edu/pipermail/concurrency-interest/2014-May/012652.html),但到目前为止我没有得到确认).
证明了僵局
以下代码将为DEADLOCK
// Outer loop IntStream.range(0,numberOfTasksInOuterLoop).parallel().forEach(i -> { doWork(); synchronized(this) { // Inner loop IntStream.range(0,numberOfTasksInInnerLoop).parallel().forEach(j -> { doWork(); }); } });
其中numberOfTasksInOuterLoop = 24
,numberOfTasksInInnerLoop = 240
,outerLoopOverheadFactor = 10000
和doWork
一些无状态的CPU刻录机.
您可以在http://svn.finmath.net/finmath%20experiments/trunk/src/net/finmath/experiments/concurrency/NestedParallelForEachAndSynchronization.java中找到完整的演示代码 (有关更详细的说明,请参阅JavaDoc).
这种行为有望吗?请注意,有关Java并行流的文档未提及嵌套或同步的任何问题.此外,未提及使用公共fork-join-pool的事实.
更新
关于性能问题的另一个测试可以在http://svn.finmath.net/finmath%20experiments/trunk/src/net/finmath/experiments/concurrency/NestedParallelForEachBenchmark.java找到 - 这个测试没有任何阻塞操作(没有线程) .sleep和not synchronized).我在这里写了一些更多的评论:http://christian-fries.de/blog/files/2014-nested-java-8-parallel-foreach.html
更新2
似乎这个问题和更严重的带有信号量的DEADLOCK已在Java8 u40中得到修复.