"Loop unswitching" 优化无效

"Loop unswitching" optimization is not working

听说Java支持"Loop Unswitching",所以就简单在JMH里测试了一下

我以为他们在 JIT 之后会完全一样。这是为什么?

private final int TIMES = 1_000_000;
private boolean bool;
private Random r = new Random(93);

@Setup(Level.Invocation)
public void fresh() {
    bool = r.nextBoolean();
}

@Benchmark
public void test1(Blackhole bh) {
    for (int i = 0; i < TIMES; i++) {
        if (bool) {
            bh.consume(1);
        } else {
            bh.consume(2);
        }
    }
}

@Benchmark
public void test2(Blackhole bh) {
    if (bool) {
        for (int i = 0; i < TIMES; i++) {
            bh.consume(1);
        }
    } else {
        for (int i = 0; i < TIMES; i++) {
            bh.consume(2);
        }
    }
}

测试结果

Benchmark              Mode  Cnt     Score   Error  Units
LoopUnswitching.test1  avgt   25  1995.192 ± 3.497  us/op
LoopUnswitching.test2  avgt   25  1644.951 ± 4.904  us/op

测试环境

# JMH version: 1.21
# VM version: JDK 1.8.0_222, OpenJDK 64-Bit Server VM, 25.222-b10

JMH 禁用 Blackhole.consume 方法的内联。非内联方法对于 JVM 来说是一个黑盒子——编译器不知道此类方法是否修改字段、抛出异常、将其注册为垃圾等。JIT 编译器无法在此类方法调用中应用许多优化。 (假设一个黑盒方法使用Reflection修改bool字段,这样loop unswitching就失效了)

HotSpot JVM 仍然支持当编译范围包括整个循环体时循环取消切换,并且已知条件在整个循环中保持不变。

考虑修改后的基准:

@State(Scope.Benchmark)
public class LoopUnswitching {
    private static final int TIMES = 10_000;

    private final Random r = new Random(93);
    private final int[] x = r.ints(TIMES).toArray();
    private final int[] y = r.ints(TIMES).toArray();

    private boolean bool;

    @Setup(Level.Invocation)
    public void setup() {
        bool = r.nextBoolean();
    }

    @Benchmark
    public int test1() {
        int sum = 0;
        for (int i = 0; i < TIMES; i++) {
            if (bool) {
                sum += x[i];
            } else {
                sum += y[i];
            }
        }
        return sum;
    }

    @Benchmark
    public int test2() {
        int sum = 0;
        if (bool) {
            for (int i = 0; i < TIMES; i++) {
                sum += x[i];
            }
        } else {
            for (int i = 0; i < TIMES; i++) {
                sum += y[i];
            }
        }
        return sum;
    }
}

在这种情况下,test1test2 的性能相似:

Benchmark              Mode  Cnt     Score   Error  Units
LoopUnswitching.test1  avgt   10  2910,432 ± 3,287  ns/op
LoopUnswitching.test2  avgt   10  2912,922 ± 9,367  ns/op