【问题标题】:"Loop unswitching" optimization is not working“循环取消切换”优化不起作用
【发布时间】:2020-03-25 15:32:32
【问题描述】:

听说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

【问题讨论】:

    标签: java performance jit


    【解决方案1】:

    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
    

    【讨论】:

      猜你喜欢
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-09
      • 1970-01-01
      • 2020-04-19
      • 2013-03-01
      • 2011-10-24
      相关资源
      最近更新 更多