我试图通过对带有 JMH 的数组的顺序/随机读取进行基准测试来观察 CPU 缓存空间局部性的影响。有趣的是,结果几乎相同。
所以我想知道,这是正确的 JMH 方法吗?
下面是我用过的测试类
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(MyBenchmark.N)
public class MyBenchmark {
/*
* # JMH version: 1.21
* # VM version: JDK 1.8.0_91, Java HotSpot(TM) 64-Bit Server VM, 25.91-b15
* # VM invoker: D:\jdk1.8.0_91\jre\bin\java.exe
* # VM options: <none>
* # Warmup: 5 iterations, 10 s each
* # Measurement: 5 iterations, 10 s each
* # Timeout: 10 min per iteration
* # Threads: 1 thread, will synchronize iterations
* # Benchmark mode: Average time, time/op
*
* Benchmark Mode Cnt Score Error Units
* MyBenchmark.randomAccess avgt 25 7,930 ± 0,378 ns/op
* MyBenchmark.serialAccess avgt 25 7,721 ± 0,081 ns/op
*/
static final int N = 1_000;
@State(Scope.Benchmark)
public static class Common {
int[] data = new int[N];
int[] serialAccessOrder = new int[N];
int[] randomAccessOrder = new int[N];
public Common() {
Random r = new Random(11234);
for (int i=0; i<N; i++) {
data[i] = r.nextInt(N);
serialAccessOrder[i] = i;
randomAccessOrder[i] = data[i];
}
}
}
@Benchmark
public void serialAccess(Blackhole bh, Common common) {
for (int i=0; i<N; i++) {
bh.consume(common.data[common.serialAccessOrder[i]]);
}
}
@Benchmark
public void randomAccess(Blackhole bh, Common common) {
for (int i=0; i<N; i++) {
bh.consume(common.data[common.randomAccessOrder[i]]);
}
}
}
更新: 原来 N 太小了(1_000 * 4 bytes/int ~= 4KB)很可能整个数组都被缓存了。将 N 增加到 1_000_000 会产生更直观的结果:
Benchmark Mode Cnt Score Error Units
MyBenchmark.randomAccess avgt 25 20,426 ± 0,678 ns/op
MyBenchmark.serialAccess avgt 25 6,762 ± 0,252 ns/op