6

因此,我尝试使用微基准测试,选择了 JMH,阅读了一些文章。JMH 如何测量低于系统计时器粒度的方法的执行?

更详细的解释:

这些是我正在运行的基准测试(方法名称不言自明):

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 200, timeUnit = TimeUnit.NANOSECONDS)
@Measurement(iterations = 20, time = 200, timeUnit = TimeUnit.NANOSECONDS)
public class RandomBenchmark {

    public long lastValue;

    @Benchmark
    @Fork(1)
    public void blankMethod() {
    }

    @Benchmark
    @Fork(1)
    public void simpleMethod(Blackhole blackhole) {
        int i = 0;
        blackhole.consume(i++);
    }

    @Benchmark
    @Fork(1)
    public void granularityMethod(Blackhole blackhole) {
        long initialTime = System.nanoTime();
        long measuredTime;
        do {
            measuredTime = System.nanoTime();
        } while (measuredTime == initialTime);
        blackhole.consume(measuredTime);
    }
}

以下是结果:

# Run complete. Total time: 00:00:02

Benchmark                          Mode  Cnt    Score    Error  Units
RandomBenchmark.blankMethod        avgt   20    0,887 ?  0,274  ns/op
RandomBenchmark.granularityMethod  avgt   20  407,002 ? 26,297  ns/op
RandomBenchmark.simpleMethod       avgt   20    6,979 ?  0,743  ns/op

目前在 Windows 7 上运行,并且正如在各种文章中所描述的那样,它具有很大的粒度 (407 ns)。用下面的基本代码检查它确实是每 ~400ns 出现一个新的计时器值:

    final int sampleSize = 100;
    long[] timeMarks = new long[sampleSize];
    for (int i=0; i < sampleSize; i++) {
        timeMarks[i] = System.nanoTime();
    }
    for (long timeMark : timeMarks) {
        System.out.println(timeMark);
    }

很难完全理解生成的方法究竟是如何工作的,但是通过反编译的 JMH 生成的代码,它似乎在执行前后使用相同的 System.nanoTime() 并测量差异。当粒度为 400 ns 时,它如何能够测量几纳秒的方法执行?

4

1 回答 1

3

你是完全正确的。您无法测量比系统计时器粒度更快的东西。

JMH 不会测量对 bechmark 方法的每次调用。它在迭代开始前调用 System.nanotime(),执行基准方法 X 次,并在迭代后再次调用 System.nanotime()。结果是时间差/操作数(可能您在方法上使用@OperationsPerInvocation 每次调用指定超过 1 个操作)。

Aleksey Shipilev 在他的文章Nanotrusting the Nanotime中讨论了 Nanotime 的测量问题。“延迟”部分包含一个代码示例,展示了 JMH 如何测量一个基准迭代。

于 2015-08-05T16:38:38.183 回答