数値の合計のパフォーマンスをテストするためにLongStream
のrangeClosed
を使用していました。 JMHで性能をテストしたところ、以下のような結果になりました。
_@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 1, jvmArgs = {"-Xms4G", "-Xmx4G"})
@State(Scope.Benchmark)
@Warmup(iterations = 10, time = 10)
@Measurement(iterations = 10, time = 10)
public class ParallelStreamBenchmark {
private static final long N = 10000000L;
@Benchmark
public long sequentialSum() {
return Stream.iterate(1L, i -> i + 1).limit(N).reduce(0L, Long::sum);
}
@Benchmark
public long parallelSum() {
return Stream.iterate(1L, i -> i + 1).limit(N).parallel().reduce(0L, Long::sum);
}
@Benchmark
public long rangedReduceSum() {
return LongStream.rangeClosed(1, N).reduce(0, Long::sum);
}
@Benchmark
public long rangedSum() {
return LongStream.rangeClosed(1, N).sum();
}
@Benchmark
public long parallelRangedReduceSum() {
return LongStream.rangeClosed(1, N).parallel().reduce(0L, Long::sum);
}
@Benchmark
public long parallelRangedSum() {
return LongStream.rangeClosed(1, N).parallel().sum();
}
@TearDown(Level.Invocation)
public void tearDown() {
System.gc();
}
_
_Benchmark Mode Cnt Score Error Units
ParallelStreamBenchmark.parallelRangedReduceSum avgt 10 7.895 ± 0.450 ms/op
ParallelStreamBenchmark.parallelRangedSum avgt 10 1.124 ± 0.165 ms/op
ParallelStreamBenchmark.rangedReduceSum avgt 10 6.832 ± 0.165 ms/op
ParallelStreamBenchmark.rangedSum avgt 10 21.564 ± 0.831 ms/op
_
rangedReduceSum
とrangedSum
の違いは、内部関数sum()のみが使用されることです。パフォーマンスにそれほど大きな違いがあるのはなぜですか?
sum()
関数が最終的にreduce(0, Long::sum)
を使用することを確認した後、rangedReduceSum
メソッドでreduce(0, Long::sum)
を使用することと同じではありませんか?
OPと同じタスクを実行しましたが、まったく同じ結果を再現できます。2番目のタスクは3倍遅くなります。しかし、ウォームアップを1回だけに変更すると、物事が面白くなります。
# Benchmark: test.ParallelStreamBenchmark.rangedReduceSum
# Warmup Iteration 1: 3.619 ms/op
Iteration 1: 3.931 ms/op
Iteration 2: 3.927 ms/op
Iteration 3: 3.834 ms/op
Iteration 4: 4.006 ms/op
Iteration 5: 4.605 ms/op
Iteration 6: 6.454 ms/op
Iteration 7: 6.466 ms/op
Iteration 8: 6.328 ms/op
Iteration 9: 6.370 ms/op
Iteration 10: 6.244 ms/op
# Benchmark: test.ParallelStreamBenchmark.rangedSum
# Warmup Iteration 1: 3.971 ms/op
Iteration 1: 4.034 ms/op
Iteration 2: 3.970 ms/op
Iteration 3: 3.957 ms/op
Iteration 4: 4.024 ms/op
Iteration 5: 4.278 ms/op
Iteration 6: 19.302 ms/op
Iteration 7: 19.132 ms/op
Iteration 8: 19.189 ms/op
Iteration 9: 18.842 ms/op
Iteration 10: 18.292 ms/op
Benchmark Mode Cnt Score Error Units
ParallelStreamBenchmark.rangedReduceSum avgt 10 5.216 ± 1.871 ms/op
ParallelStreamBenchmark.rangedSum avgt 10 11.502 ± 11.879 ms/op
各タスクはすべて、5回目の反復後に大幅にスローダウンします。 2番目のタスクでは、5回目の反復の直後に3倍の速度で減速します。ウォームアップを反復として数える場合、10回の反復の後、すでに低速で開始することは理にかなっています。ベンチマークライブラリのバグのようですが、GCではうまく機能しません。しかし、警告が言うように、そのような場合のベンチマーク結果は参照用です。