“循环取消切换”优化不起作用

时间:2019-11-30 09:38:11

标签: java performance jit

我听说Java支持“循环取消切换”,所以我只是在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

1 个答案:

答案 0 :(得分:3)

JMH禁用Blackhole.consume方法的内联。非内联方法是JVM的黑匣子-编译器不知道该方法是否会修改字段,引发异常,将其注册为垃圾等。JIT编译器无法在此类方法调用中应用许多优化。 (想象一下,黑盒方法使用反射来修改bool字段,因此循环取消切换将无效。)

当编译范围包括整个循环主体时,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
相关问题