Java 8:stream()或parallelStream()的首次使用非常缓慢-在实践中的使用很有意义



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

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

所以我的问题是:

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

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

简要总结:并行流比顺序流启动成本更高。如果您的工作负载是可拆分的,并且您有多个CPU核心可以承担这个问题,并且每个元素的成本不是不合理的小,那么您将在足够大的工作负载下获得并行加速。(很多条件语句都是这样吗?)哦,而且你还必须小心基准测试。

StackOverflow充斥着各种问题,试图将几个整数并行相加,然后声称并行流不好,因为它们不能提供任何加速。我甚至不会去链接他们。

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

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

对于数组上的循环特别快,因为唯一的"设置"是初始化循环计数器和寄存器中的限制值。JIT编译器也可以带来许多循环优化。序列流在数组上击败for循环的情况很少见,尽管这是可能发生的。

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

最后,性能并不是唯一的考虑因素。还有可读性和可维护性。流和lambda的东西最初可能是新的和不熟悉的,但它在简化和清理代码方面有很大的潜力。例如,请参阅我对另一个问题的回答。

最新更新