使用嵌套的Intstream循环时,Java 8的性能非常糟糕

Jos*_*ung 14 java performance java-8 java-stream

在阅读了Java 8的java.util.stream.Intstream之后,我一直在用流替换一些传统的循环.不幸的是,在处理嵌套循环时遇到了一些性能问题.

正如预期的那样,以下代码在我的机器中运行大约47毫秒:

IntStream.range(0, 1000000000).forEach(i -> {});
Run Code Online (Sandbox Code Playgroud)

但是,嵌套另一个IntStream hyper会将执行时间膨胀到大约10,458 ms - 即:

IntStream.range(0, 1000000000).forEach(i -> {
    IntStream.range(0, 1).forEach(j -> {});
});
Run Code Online (Sandbox Code Playgroud)

这是我的滥用案例,还是这个问题将来可以解决?

编辑:为了进行比较,以下代码使用传统的内部循环运行得更快(在1,801毫秒内).因此,即使考虑到优化,使用内部IntStream似乎也会有更多的开销?

final long[] random = {1};
IntStream.range(0, 1000000000).forEach(i -> {
    for (int j = 0; j < 1; j++) {
        random[0] += i;
    }
});
Run Code Online (Sandbox Code Playgroud)

Tag*_*eev 8

这不是第二种情况下糟糕的表现.在第一种情况下,它实际上是令人难以置信的出色表现.看,你迭代了超过十亿个元素,迭代只需要47毫秒.因此,在一秒钟内你就可以迭代超过1000/47 = 210亿个元素!CPU的频率大约为3 GHz,因此您可以在单CPU周期内迭代7个元素!这种优化由JIT编译器执行,用于非常简单的循环(实际上它在死代码消除期间绝对优化).然而,你不会赚钱写空循环.如果您添加至少一些非平凡的逻辑,一些优化将关闭或变得不那么有效,因此您将有显着的性能下降.

我建议你对真实代码进行测试,并为最慢的部分分析你的应用程序.人工实例与生产代码的实际性能没有任何共同之处.

  • 在这种特殊情况下,我不认为循环是展开和矢量化的.经过一定的预热阶段后,它就完全被移除了...... (3认同)

Ale*_* K. 3

来自java文档

void forEach(IntConsumer action) 对此流的每个元素执行一个操作。这是终端操作。

终端操作(例如 Stream.forEach 或 IntStream.sum)可能会遍历流以产生结果或副作用。执行完终端操作后,流管道被认为已消耗,不能再使用;如果需要再次遍历同一个数据源,则必须返回数据源获取新的流。几乎在所有情况下,终端操作都是急切的,在返回之前完成对数据源的遍历和对管道的处理。只有终端操作 iterator() 和 spliterator() 不是;这些是作为“逃生舱口”提供的,以便在现有操作不足以完成任务的情况下启用任意客户端控制的管道遍历。

创建大量流会产生开销。您是否尝试过使用探查器运行代码?