Я пытался наблюдать влияние пространственной локализации кэша ЦП, сравнивая последовательные / случайные чтения в массив с JMH. Интересно, что результаты почти одинаковы.
Так что мне интересно, это правильный подход 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 байта / int ~ = 4 КБ), скорее всего, весь массив был кэширован. Увеличение 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