【发布时间】:2014-12-31 02:12:17
【问题描述】:
我正在重写一个应用程序,该应用程序涉及使用 Java 8 处理大约 1000 万个对象,我注意到流可以使应用程序减慢 25%。有趣的是,当我的集合也为空时也会发生这种情况,因此它是流的恒定初始化时间。要重现该问题,请考虑以下代码:
long start = System.nanoTime();
for (int i = 0; i < 10_000_000; i++) {
Set<String> set = Collections.emptySet();
set.stream().forEach(s -> System.out.println(s));
}
long end = System.nanoTime();
System.out.println((end - start)/1000_000);
start = System.nanoTime();
for (int i = 0; i < 10_000_000; i++) {
Set<String> set = Collections.emptySet();
for (String s : set) {
System.out.println(s);
}
}
end = System.nanoTime();
System.out.println((end - start)/1000_000);
结果如下:224 vs. 5 ms。
如果我直接在设置上使用forEach,即set.forEach(),结果将是:12 vs 5ms。
最后,如果我在外面创建一次闭包
Consumer<? super String> consumer = s -> System.out.println(s);
并使用set.forEach(c) 结果将是 7 vs 5 ms。
当然,数字很小,我的基准测试非常原始,但是这个例子是否表明初始化流和闭包有开销?
(实际上,由于set 是空的,在这种情况下,闭包的初始化成本应该不重要,但是,我应该考虑提前创建闭包而不是即时创建闭包)
【问题讨论】:
-
我也遇到过这个问题,让我非常沮丧和失望。制作一百万个条目,对它们进行随机播放,并尝试使用常规 for 循环和使用 java 8 的 findfirst 找到第一个,并尝试为它们计时声明
-
Clojure != 闭包,
Consumer不是闭包。 en.wikipedia.org/wiki/Closure_%28computer_programming%29 -
发布与valid performance benchmark 讨论 Lambda 与 Anon 类的帖子
-
肯定有一些启动开销。但在许多情况下,一旦支付了启动开销,流就会比相应的 for 循环快。所以你应该用实际数据做一些测试。 (此外,您的测量方法完全失效,因此您得到的大多是无用的数字。)简短的回答是“是的,有一些”——但如果您想真正了解,请尝试更新您的应用并使用真实的测量其性能数据。
-
看看 Sergey Kukcenko 在 2013 年 JVM 语言峰会上的演讲(经过严格测量)。几乎所有“行为良好”的流管道(例如 filter-map-reduce),其来源都是一旦您有足够的数据来克服启动差异,Collection 将优于相应的 for 循环,因为元素访问路径要快得多;与迭代器相比,流的 O(n) 项的系数要小。最终占主导地位。
标签: java lambda java-8 java-stream