Java 8 : First use of stream() or parallelStream() very slow - Usage in practice meaningful?

标签 java java-8 java-stream

在过去的几天里,我用 Java 8 中的外部迭代、流和并行流进行了一些测试,并测量了执行时间的持续时间。我还阅读了我必须考虑的预热时间。但是还有一个问题。

当我第一次对集合调用方法 stream()parallelStream() 时,执行时间比外部迭代长。我已经知道,当我在同一集合上更频繁地调用 stream()parallelStream() 并计算执行时间时,parallelStream() 确实比外部迭代更快。但是由于在实践中一个集合通常也只迭代一次,所以我只看到使用流或并行流的缺点。

所以我的问题是:

如果我只迭代一次集合,使用流或 parallelStream() 是个好主意,还是执行时间总是比外部迭代长?

最佳答案

完全巧合的是(显然),Doug Lea、Brian Goetz 和其他几个人编写了一份名为 Stream Parallel Guidance 的文档。 . (这只是一个草案。)它确实对何时使用并行流与顺序流进行了一些有用的讨论。

简要总结:并行流的启动成本高于顺序流。如果您的工作负载是可拆分的,并且您有多个 CPU 内核可以解决该问题,并且如果每个元素的成本不是小得离谱,那么您将在足够大的工作负载下获得并行加速。 (对于很多条件语句来说怎么样?)哦,你还必须小心基准测试。

StackOverflow 充斥着试图并行添加几个整数然后声称并行流不好的问题,因为它们不提供任何加速。我什至懒得链接到他们。

现在,您问过“外部迭代”(基本上是 for 循环)与流、并行或顺序。我认为考虑并行流与顺序流很重要,正如我在上面所做的那样。这将有助于为进一步的决定提供信息。显然,如果您有可能需要并行运行事物,那么您可能应该使用流,即使您最初是按顺序开始的。

即使您不打算并行,在 for 循环和顺序流之间仍然存在许多注意事项。与传统循环相比,流有一定的开销——尤其是数组上的 for 循环。但这通常会在工作量上分摊。即使集合只迭代一次,如果集合中的元素数量足够大,也会发生设置的摊销。例如,如果集合有 10 个元素,流的额外设置成本可能不值得。如果集合有 10,000 个元素,情况可能会有所不同。

数组上的 For 循环特别快,因为唯一的“设置”是初始化循环计数器和寄存器中的限制值。 JIT 编译器也可以带来许多循环优化。顺序流很少会在数组上击败 for 循环,尽管它可能会发生。

集合上的 For 循环通常涉及创建一个迭代器,因此比基于数组的循环有更多的开销。特别是,迭代器上的每次迭代都涉及对 hasNextnext 的方法调用,而流可以通过单个方法调用获取每个元素。出于这个原因,有时顺序流可以击败基于迭代器的循环(给定正确的每个元素工作负载、足够多的元素等)。因此,即使流有一些设置成本,它也有可能最终比传统的 for 循环运行得更快。

最后,性能并不是唯一的考虑因素。还有可读性和可维护性。流和 lambda 的东西最初可能是新的和不熟悉的,但它具有简化和清理代码的巨大潜力。参见 my answer例如,另一个问题。

关于Java 8 : First use of stream() or parallelStream() very slow - Usage in practice meaningful?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25625250/

相关文章:

java - 使用android studio IDE隐藏jar中方法的实现

如果第二个列表的一项满足条件并返回新列表,则 Java 8 foreach 添加到新列表

java - partitioningBy 必须生成包含 true 和 false 条目的映射吗?

java-8 - Java 8 流和 RxJava 可观察量之间的区别

Java 8 lambdas 分组缩减和映射

java - 如何在opencv java中裁剪检测到的人脸图像

java - 我怎样才能制作以下对象?

java - 如何使用 java 8 和流将属性值从列表第一个元素传播到所有列表元素?

java - onCreate 中 getWidth() 返回 0

java - do-while with Java8-Optional