【问题标题】:Java parallel stream not working as expectedJava 并行流未按预期工作
【发布时间】:2020-02-25 21:29:04
【问题描述】:

我有以下代码:

Map<String, Person> targetPerson = targetPersonList
                                     .stream()
                                     .collect(toMap(Person::getKey,  Function.identity()));

其中 targetPersonList 是一个相当大的列表,上面的代码大约需要 38 分钟才能完成。 所以我认为下面的代码应该加快一点速度

Map<String, Person> targetPerson = targetPersonList
                                     .parallelStream()
                                     .collect(toMap(Person::getKey, Function.identity()));

实际上正好相反,“平行”的部分,需要 1 小时 20 分钟。我有一个 Core i7 第 8 代,它应该有 6 个核心和 12 个线程,那么有什么问题呢?我对并行流的理解有什么根本错误吗?

【问题讨论】:

  • targetPersonList 的运行时类型是什么?它包含多少元素?使用这个简单的管道,并行流的性能更差也就不足为奇了。令人惊讶的是,管道在单线程中运行需要 40 分钟,但在作为并行流运行时,其执行时间仍会增加一倍。

标签: java parallel-processing java-stream


【解决方案1】:

填写HashMap 需要 38 分钟,这是一段不寻常的长时间。这表明Person::getKey 正在执行昂贵的构造,或者结果是对象的hashCodeequals 实现不是最优的。

在我的机器上,用合理的hashCodeequals 实现来填充一千万个元素的地图不到一秒,亿万仍然只需要几秒钟,然后,内存消耗就成了问题。

也就是说,并行流的较差性能并不令人意外。正如“Should I always use a parallel stream when possible?”中所讨论的,并行处理有一些固定的开销,您需要一些重要的(每个元素)工作负载才能获得比开销更大的好处。

在您的具体示例中,根本没有任何好处。

并行collect 操作通过将流元素分成块来工作,以由不同的工作线程处理。他们每个人都会创建一个新的本地容器,如果toMap与最终结果相同类型的map,然后,每个线程会将元素累积到其本地容器中,即将值放入map中,当两个worker线程已完成工作,部分结果将被合并,这意味着将一张地图的所有元素放入另一张地图。

由于您没有过滤操作并且没有合并功能意味着所有键都是唯一的,因此很容易得出结论,在最好的情况下您有两个工作线程填充两个相同的映射大小完全平行,然后将其中一张地图放入另一张地图中,所花费的时间与之前的并行处理所节省的时间一样多。

您的示例也不包括潜在昂贵的中间操作,因此只有当 Person::getKey 很昂贵时,并行处理才能降低成本。

正如this answer 中所讨论的那样,使用toConcurrentMap 而不是toMap 可以改善这种情况,因为它允许跳过合并操作并且拥有所有唯一键意味着当所有工作线程放入一个时竞争非常低地图。

但是,值得调查性能问题的实际原因。当问题是关键对象的hashCodeequals 实现时,修复它将获得更多收益。此外,并发无法解决与几乎满堆相关的问题。

最后,toConcurrentMap 返回一个并发映射,这可能会给后续处理带来更高的成本,即使您不打算将此映射用于多线程。

【讨论】:

    【解决方案2】:

    如果您有大量运算符,则使用并行流是有益的。例如,一个长时间执行的 map 函数。在您的情况下,最好不要也使用流,因为它只会减慢速度。

    不过,我有一套建议。

    1. 由于您可能想要使用 HashMap,请确保实现并缓存 hashCode() 函数的结果。
    2. 使用传递初始容量new HashMap&lt;&gt;(targetPersonList.size());的构造函数初始化您的地图
    3. 如果所有值都已预先计算,则使用 for 循环并插入每个元素。

    【讨论】:

      猜你喜欢
      • 2011-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-14
      • 2012-06-29
      相关资源
      最近更新 更多