【问题标题】:Key indicators that a Java 8 stream will run slower than a for loop?Java 8 流运行速度比 for 循环慢的关键指标?
【发布时间】:2016-04-10 11:27:29
【问题描述】:

在大多数情况下,Java 8 流允许的代码比老式的for 循环更具可读性。但是,根据我自己的经验和阅读的内容,使用流而不是 for 循环可能会导致性能下降(或偶尔提高),这有时难以预测。

在大型项目中,为每个循环编写基准测试似乎并不可行,因此在决定是否将for 循环替换为流时,关键因素是什么(例如,集合的预期大小、通过过滤删除的值的预期百分比、迭代操作的复杂性、归约或聚合的类型等)这可能表明将导致的性能变化?

注意:这是my earlier question 的缩小版,由于过于宽泛而被关闭(并且并行流的各个方面都很好地涵盖了in another SO question),所以让我们将其限制为顺序流.

【问题讨论】:

  • 对于 99% 的情况,答案很简单:编写清晰、可读、可维护且明显正确的代码。虽然人们可能会无休止地担心性能,但在大多数情况下,以美元衡量的实际成本差异为零(因为大多数代码“足够快”。)另一方面,出错可能会产生真正的美元成本. (而且,如果您属于需要担心性能的不到 1%,那么您已经知道这一点,并且您已经在测量方法上进行了大量投资。)
  • 感谢那些回答或评论的人。我的问题基于法国杂志(Programmez,2016 年 1 月)中的一篇文章,其中一些顾问将 B2C 网站的代码分支并重构以用流替换许多 for 循环。根据性能测试(在最坏的情况下方法执行时间大约加倍),他们无法说服产品负责人继续在主分支中进行重构。很难向产品负责人推销“我们将花一周时间改进代码。我们不会添加任何功能,产品运行速度会更慢。”

标签: java lambda java-8 java-stream


【解决方案1】:

这不仅“为每个循环编写基准测试不可行”,而且适得其反。一个特定的、特定于应用程序的循环在放入微基准测试时可能会执行完全不同的操作。

对于实际应用,标准的优化规则适用:不要这样做。只需编写更具可读性且仅在存在性能问题时,分析整个应用程序以检查特定循环或流的使用是否真的是瓶颈。只有在这种情况下,您才可以尝试在特定瓶颈处切换两个习语,看看是否有区别。

在大多数情况下,它不会。如果存在真正的性能问题,它将源于操作类型,例如执行具有O(n²) 时间复杂度等的嵌套迭代。此类问题不取决于您是使用Stream 还是for 循环,并且这两个习语之间的微小性能差异不会改变您的代码尺度。

【讨论】:

  • 经过一番思考,我决定选择这个作为答案。该问题收到了 2 个写得很好、有理有据的答案,以及来自世界知名专家的明智评论。我想要一个可以在代码审查中使用的答案来捕获 2x 减速预提交,但也许这是不可能的。我之所以选择这个答案,是因为在性能敏感的代码中用流替换循环的最佳方法显然是以增量方式进行,如果自动端到端测试检测到性能问题,可以很容易地退出。跨度>
【解决方案2】:

流和循环之间的一般速度差异不大;它们的优点/缺点是针对特定问题的。您是否选择其中一个应该(主要)取决于代码的可读性。有关一些性能比较,请参阅 Benchmark1Benchmark2,您可以在其中注意到 Brian Goetz 对其中一个答案的评论:

您关于性能的结论虽然有效,但被夸大了。在很多情况下,流代码比迭代代码更快,主要是因为流的每个元素访问成本比普通迭代器便宜。在许多情况下,流版本内联到与手写版本等效的内容。当然,魔鬼在细节中。任何给定的代码都可能表现不同。

除此之外,只需确保在进行基准测试时使用JMH

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    • 2012-03-03
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-02
    相关资源
    最近更新 更多