JMH使用java17,无死码消除

JMH using java 17, no dead code elimination

我 运行 样本 JHM 基准,它假设显示死代码消除。为简洁起见,代码从 jhm github sample.

重写
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(1)
public class Sample08DeadCode {

    private double x = Math.PI;

    @Benchmark
    public void benchmark() {}

    @Benchmark
    public void measureIncorrect() { Math.log(x); }

    @Benchmark
    public double measureCorrect() { return Math.log(x); }
}

运行 使用 JDK 1.8.0_211,Java HotSpot(TM) 64 位服务器 VM,25.211-b12 产生以下结果:

Benchmark                          Mode  Cnt   Score   Error  Units
Sample08DeadCode.benchmark         avgt    5   0,229 ± 0,018  ns/op
Sample08DeadCode.measureCorrect    avgt    5  12,013 ± 0,047  ns/op
Sample08DeadCode.measureIncorrect  avgt    5   0,228 ± 0,016  ns/op

但使用 java JDK 17.0.2, Java HotSpot(TM) 64-Bit Server VM, 17.0.2+8-LTS-86 结果没有符号死代码消除:

Benchmark                          Mode  Cnt  Score   Error  Units
Sample08DeadCode.benchmark         avgt    5  0,341 ± 0,004  ns/op
Sample08DeadCode.measureCorrect    avgt    5  6,244 ± 0,072  ns/op
Sample08DeadCode.measureIncorrect  avgt    5  6,263 ± 0,094  ns/op

为什么 measureIncorrect() 方法没有使用 java 17 优化?

这些示例取决于 JDK 内部结构。

看起来自从 JDK 9 和 JDK-8152907 以来,Math.log 不再内化为 C2 中间表示。相反,直接调用快速 LIBM-backed 存根。对于实际使用结果的代码,这通常更快。请注意 measureCorrect 在 JDK 17 输出中如何更快。

但对于 JMH 示例,它限制了围绕 Math.log 的编译器优化,死代码/折叠示例无法正常工作。修复它以制作不依赖 JDK 内部结构且没有充分理由的样本,而是使用自定义的书面有效负载。

这是在 JMH 中完成的: