JMH - 为什么JIT编译器无法消除我的死代码

3

我写了两个基准测试来展示JIT在编写优秀基准测试时可能会成为一个问题(请忽略我在此处没有使用@State):

@Fork(value = 1)
@Warmup(iterations = 2, time = 10)
@Measurement(iterations = 3, time = 2)
@BenchmarkMode(Mode.AverageTime)
public class DeadCodeTraps {

    @Benchmark
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public static void summaryStatistics_standardDeviationForFourNumbers() {
        final SummaryStatistics summaryStatistics = new SummaryStatistics();
        summaryStatistics.addValue(10.0);
        summaryStatistics.addValue(20.0);
        summaryStatistics.addValue(30.0);
        summaryStatistics.addValue(40.0);
        summaryStatistics.getStandardDeviation();
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public static void summaryStatistics_standardDeviationForTenNumbers() {
        final SummaryStatistics summaryStatistics = new SummaryStatistics();
        summaryStatistics.addValue(10.0);
        summaryStatistics.addValue(20.0);
        summaryStatistics.addValue(30.0);
        summaryStatistics.addValue(40.0);
        summaryStatistics.addValue(50.0);
        summaryStatistics.addValue(60.0);
        summaryStatistics.addValue(70.0);
        summaryStatistics.addValue(80.0);
        summaryStatistics.addValue(90.0);
        summaryStatistics.addValue(100.0);
        summaryStatistics.getStandardDeviation();
    }

}

我以为JIT会消除死代码,所以两个方法将同时执行。但最终,我得到了以下结果: summaryStatistics_standardDeviationForFourNumbers 0.158 ± 0.046 DeadCodeTraps.summaryStatistics_standardDeviationForTenNumbers 0.359 ± 0.294
为什么JIT没有进行优化?summaryStatistics.getStandardDeviation();的结果在方法外部没有被使用也没有被返回。
(我正在使用OpenJDK编译版本10.0.2+13-Ubuntu-1ubuntu0.18.04.4)

1
这里哪段代码是无用的?你期望这两段代码执行时间相同吗? - kutschkem
你具体期望什么?我对那个结果一点也不感到惊讶。 - luk2302
summaryStatistics.getStandardDeviation(); 在方法范围之外没有被使用或返回,因此在JIT优化后保留该代码是没有意义的。或者说它可能会保留该代码,因为无法确定该代码是否具有任何副作用。 - ByeBye
1个回答

6
如果您谈论的是Apache Commons Math SummaryStatistics类,那么它是一个庞大的类。它的构建肯定不会被内联。要了解原因,请使用-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:-BackgroundCompilation运行。
死代码消除发生在内联之后。未使用的对象将向后传播,但是非内联的构造函数将中断链路,因为JIT优化器无法再确定是否存在副作用。
换句话说,您期望被消除的代码太大了。

是的,你说得对。一开始,我想使用java.util中的DoubleSummaryStatistics,但我在导入方面搞砸了。在DoubleSummaryStatistics的情况下,它被内联并且与空方法几乎具有相同的结果。谢谢! - ByeBye

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接