【发布时间】:2019-06-07 09:40:00
【问题描述】:
此代码对计算double[] 元素倒数和的 3 种不同方法进行基准测试。
-
for-loop - Java 8 流
-
colt数学库
使用简单 for 循环的计算比使用流的计算快 400 倍的原因是什么? (或者在基准测试代码中有什么需要改进的地方?或者使用流计算的更快的方法?)
代码:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import cern.colt.list.DoubleArrayList;
import cern.jet.stat.Descriptive;
import org.openjdk.jmh.annotations.*;
@State(Scope.Thread)
public class MyBenchmark {
public static double[] array;
static {
int num_of_elements = 100;
array = new double[num_of_elements];
for (int i = 0; i < num_of_elements; i++) {
array[i] = i+1;
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void testInversionSumForLoop(){
double result = 0;
for (int i = 0; i < array.length; i++) {
result += 1.0/array[i];
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void testInversionSumUsingStreams(){
double result = 0;
result = Arrays.stream(array).map(d -> 1/d).sum();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void testInversionSumUsingCernColt(){
double result = Descriptive.sumOfInversions(new DoubleArrayList(array), 0, array.length-1);
}
}
结果:
/**
* Results
* Benchmark Mode Cnt Score Error Units
* MyBenchmark.testInversionSumForLoop avgt 200 1.647 ± 0.155 ns/op
* MyBenchmark.testInversionSumUsingCernColt avgt 200 603.254 ± 22.199 ns/op
* MyBenchmark.testInversionSumUsingStreams avgt 200 645.895 ± 20.833 ns/o
*/
更新:这些结果表明 Blackhome.consume 或 return 是避免 jvm 优化所必需的。
/**
* Updated results after adding Blackhole.consume
* Benchmark Mode Cnt Score Error Units
* MyBenchmark.testInversionSumForLoop avgt 200 525.498 ± 10.458 ns/op
* MyBenchmark.testInversionSumUsingCernColt avgt 200 517.930 ± 2.080 ns/op
* MyBenchmark.testInversionSumUsingStreams avgt 200 582.103 ± 3.261 ns/op
*/
oracle jdk 版本“1.8.0_181”,达尔文内核版本 17.7.0
【问题讨论】:
-
JMH 尚未使基准自动更正。此处不使用计算结果,在
loop情况下,JIT 完全删除了计算。您需要通过调用Blackhole.consume或在基准方法的末尾添加return result;来“使用”结果。 -
1.647ns 的时间太短,无法对 100 个数字求和,即每秒超过 6e+10 次操作。我认为 testInversionSumForLoop 正在被 JIT 编译器优化掉。
-
@apangin 你是对的,我错过了 Blackhole.consume 现在结果相似。
标签: java performance java-8 java-stream jmh