Java 8中的legacy for循环,流和parallelStream之间的比较

import java.util.ArrayList; import java.util.List; public class IterationBenchmark { public static void main(String args[]){ List persons = new ArrayList(); persons.add("AAA"); persons.add("BBB"); persons.add("CCC"); persons.add("DDD"); long timeMillis = System.currentTimeMillis(); for(String person : persons) System.out.println(person); System.out.println("Time taken for legacy for loop : "+ (System.currentTimeMillis() - timeMillis)); timeMillis = System.currentTimeMillis(); persons.stream().forEach(System.out::println); System.out.println("Time taken for sequence stream : "+ (System.currentTimeMillis() - timeMillis)); timeMillis = System.currentTimeMillis(); persons.parallelStream().forEach(System.out::println); System.out.println("Time taken for parallel stream : "+ (System.currentTimeMillis() - timeMillis)); } } 

输出:

 AAA BBB CCC DDD Time taken for legacy for loop : 0 AAA BBB CCC DDD Time taken for sequence stream : 49 CCC DDD AAA BBB Time taken for parallel stream : 3 

与传统for循环相比,为什么Java 8 Stream API性能非常低?

在程序中第一次调用Stream API总是很慢,因为你需要加载许多辅助类,为lambdas生成许多匿名类,并编译许多方法。 因此,通常第一次Stream操作需要几十毫秒。 连续的呼叫要快得多,并且可能超过1 us,具体取决于确切的流操作。 如果您交换并行流测试和顺序流测试,顺序流将更快。 所有努力都是由第一个来的人完成的。

让我们编写一个JMH基准来正确预热代码并独立测试所有案例:

 import java.util.concurrent.TimeUnit; import java.util.*; import java.util.stream.*; import org.openjdk.jmh.annotations.*; @Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) @Fork(3) @State(Scope.Benchmark) public class StreamTest { List persons; @Setup public void setup() { persons = new ArrayList(); persons.add("AAA"); persons.add("BBB"); persons.add("CCC"); persons.add("DDD"); } @Benchmark public void loop() { for(String person : persons) System.err.println(person); } @Benchmark public void stream() { persons.stream().forEach(System.err::println); } @Benchmark public void parallelStream() { persons.parallelStream().forEach(System.err::println); } } 

这里我们有三个测试: loopstreamparallelStream 。 请注意,我将System.out更改为System.err 。 那是因为System.out通常用于输出JMH结果。 我将System.err的输出重定向到nul ,因此结果应该更少地依赖于我的文件系统或控制台子系统(在Windows上尤其慢)。

结果是(Core i7-4702MQ CPU @ 2.2GHz,4核HT,Win7,Oracle JDK 1.8.0_40):

 Benchmark Mode Cnt Score Error Units StreamTest.loop avgt 30 42.410 ± 1.833 us/op StreamTest.parallelStream avgt 30 76.440 ± 2.073 us/op StreamTest.stream avgt 30 42.820 ± 1.389 us/op 

我们看到的是streamloop产生完全相同的结果。 差异在统计上是微不足道的。 实际上Stream API比循环慢一些,但这里最慢的部分是PrintStream 。 即使输出nul ,IO子系统与其他操作相比也非常慢。 所以我们测量的不是Stream API或循环速度,而是println速度。

另外,它是微秒,因此流版本实际上比您的测试快1000倍。

为什么parallelStream要慢得多? 仅仅因为你无法将写入并行化到同一个PrintStream ,因为它是内部同步的。 因此, parallelStream完成了将4个元素列表拆分为4个子任务所做的所有艰苦工作,在不同的线程中调度作业,正确地同步它们,但由于最慢的操作( println )不能并行执行,这绝对是徒劳的:其中一个线程正在工作,其他线程正在等待。 通常,并行化在同一个互斥锁上同步的代码是没用的(这是你的情况)。