JIT - 微优化 - 如果语句消除

Haa*_*ang 3 java jit jvm jvm-hotspot

我们假设我们有以下代码:

public static void check() {   
    if (Config.initialized) {
           ...
    }
}
Run Code Online (Sandbox Code Playgroud)

Config.initialized为开头假,只有在某个点的方法已经被JIT已经编译后变为真实.价值永远不会回归假.

我"知道"有很多非常复杂的优化正在进行(循环展开,分支预测,内联,逃逸分析等),虽然我远远没有详细了解它们,但我主要对以下内容感兴趣目前:

  1. JIT编译器是否有办法在某个时间点之后检测到if将始终为true,以便可以完全跳过检查?完全我的意思是没有变量访问,没有条件检查/ jne等...

  2. 如果JIT没有办法摆脱(从某个点开始)不必要的样本检查(我不知道它怎么可能)有什么我可以做的来支持它?我唯一的想法是重新创建类,并在初始化事件发生后从字节代码中删除不必要的代码.

我知道这是完全微观优化,即使使用像JMH这样的工具也可能很难保证,但我仍然想知道和理解.

最后但并非最不重要的:

  1. 我的理解是正确的,如果上面的方法被内联到所有那些方法将被重新编译(假设它们很热),以防某些东西发生变化,以便该check方法需要重新编译?

如果我正确理解我的JitWatch测试结果,上述问题的答案应该是:

  1. 不,没办法.总会有条件检查.
  2. 真的只能通过转型

apa*_*gin 6

  1. JIT编译器是否有办法检测在某个点之后if将始终为true

是的,如果字段是static final,并且它的持有者类已经在JIT编译器启动时初始化.显然这不适用于您的情况,因为Config.initialized无法进行static final.

  1. 有什么我可以做的来支持吗?

java.lang.invoke.MutableCallSite 救援.

本课程专为您提出的要求而设计.其setTarget方法支持在运行时重新绑定调用站点.它引起了当前编译方法的优化,并有可能在以后用新目标重新编译它.

MethodHandle用于调用MutableCallSite目标的A 可以用dynamicInvoker方法获得.请注意,MethodHandle应该是static final为了允许内联.

  1. 如果上面的方法在某处内联,那么所有这些方法都将被重新编译

是.

这是一个基准,证明该mutableCallSite方法alwaysFalse与开始时一样快,并且alwaysTrue与切换切换后一样快.我还在@Holger建议中包含了一个静态字段切换以进行比较.

package bench;

import org.openjdk.jmh.annotations.*;
import java.lang.invoke.*;
import java.util.concurrent.*;

@State(Scope.Benchmark)
public class Toggle {
    static boolean toggleField = false;

    static final MutableCallSite toggleCallSite =
            new MutableCallSite(MethodHandles.constant(boolean.class, false));

    static final MethodHandle toggleMH = toggleCallSite.dynamicInvoker();

    public void switchToggle() {
        toggleField = true;
        toggleCallSite.setTarget(MethodHandles.constant(boolean.class, true));
        MutableCallSite.syncAll(new MutableCallSite[]{toggleCallSite});
        System.out.print("*** Toggle switched *** ");
    }

    @Setup
    public void init() {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
        executor.schedule(this::switchToggle, 10100, TimeUnit.MILLISECONDS);
        executor.shutdown();
    }

    @Benchmark
    public int alwaysFalse() {
        return 0;
    }

    @Benchmark
    public int alwaysTrue() {
        return ThreadLocalRandom.current().nextInt();
    }

    @Benchmark
    public int field() {
        if (toggleField) {
            return ThreadLocalRandom.current().nextInt();
        } else {
            return 0;
        }
    }

    @Benchmark
    public int mutableCallSite() throws Throwable {
        if ((boolean) toggleMH.invokeExact()) {
            return ThreadLocalRandom.current().nextInt();
        } else {
            return 0;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过5次预热迭代和10次测量迭代运行基准测试,我得到以下结果:

# JMH version: 1.20
# VM version: JDK 1.8.0_192, VM 25.192-b12

# Benchmark: bench.Toggle.alwaysFalse

# Run progress: 0,00% complete, ETA 00:01:00
# Fork: 1 of 1
# Warmup Iteration   1: 3,875 ns/op
# Warmup Iteration   2: 3,369 ns/op
# Warmup Iteration   3: 2,699 ns/op
# Warmup Iteration   4: 2,696 ns/op
# Warmup Iteration   5: 2,703 ns/op
Iteration   1: 2,697 ns/op
Iteration   2: 2,696 ns/op
Iteration   3: 2,696 ns/op
Iteration   4: 2,706 ns/op
Iteration   5: *** Toggle switched *** 2,698 ns/op
Iteration   6: 2,698 ns/op
Iteration   7: 2,692 ns/op
Iteration   8: 2,707 ns/op
Iteration   9: 2,712 ns/op
Iteration  10: 2,702 ns/op


# Benchmark: bench.Toggle.alwaysTrue

# Run progress: 25,00% complete, ETA 00:00:48
# Fork: 1 of 1
# Warmup Iteration   1: 5,159 ns/op
# Warmup Iteration   2: 5,198 ns/op
# Warmup Iteration   3: 4,314 ns/op
# Warmup Iteration   4: 4,321 ns/op
# Warmup Iteration   5: 4,306 ns/op
Iteration   1: 4,306 ns/op
Iteration   2: 4,310 ns/op
Iteration   3: 4,297 ns/op
Iteration   4: 4,324 ns/op
Iteration   5: *** Toggle switched *** 4,356 ns/op
Iteration   6: 4,300 ns/op
Iteration   7: 4,310 ns/op
Iteration   8: 4,290 ns/op
Iteration   9: 4,297 ns/op
Iteration  10: 4,294 ns/op


# Benchmark: bench.Toggle.field

# Run progress: 50,00% complete, ETA 00:00:32
# Fork: 1 of 1
# Warmup Iteration   1: 3,596 ns/op
# Warmup Iteration   2: 3,429 ns/op
# Warmup Iteration   3: 2,973 ns/op
# Warmup Iteration   4: 2,937 ns/op
# Warmup Iteration   5: 2,934 ns/op
Iteration   1: 2,927 ns/op
Iteration   2: 2,928 ns/op
Iteration   3: 2,932 ns/op
Iteration   4: 2,929 ns/op
Iteration   5: *** Toggle switched *** 3,002 ns/op
Iteration   6: 4,887 ns/op
Iteration   7: 4,866 ns/op
Iteration   8: 4,877 ns/op
Iteration   9: 4,867 ns/op
Iteration  10: 4,877 ns/op


# Benchmark: bench.Toggle.mutableCallSite

# Run progress: 75,00% complete, ETA 00:00:16
# Fork: 1 of 1
# Warmup Iteration   1: 3,474 ns/op
# Warmup Iteration   2: 3,332 ns/op
# Warmup Iteration   3: 2,750 ns/op
# Warmup Iteration   4: 2,701 ns/op
# Warmup Iteration   5: 2,701 ns/op
Iteration   1: 2,697 ns/op
Iteration   2: 2,696 ns/op
Iteration   3: 2,699 ns/op
Iteration   4: 2,706 ns/op
Iteration   5: *** Toggle switched *** 2,771 ns/op
Iteration   6: 4,310 ns/op
Iteration   7: 4,306 ns/op
Iteration   8: 4,312 ns/op
Iteration   9: 4,317 ns/op
Iteration  10: 4,301 ns/op
Run Code Online (Sandbox Code Playgroud)