对于循环性能:具有相同值与不同值的计数器

问题描述

我有一个带有2个计数器的循环:i和j。如果它们的值相同-迭代的速度要比它们的值不同要快得多:

Benchmark                     Mode  Cnt       Score      Error  Units
FloatsArrayBenchmark.times   thrpt   20  341805.800 ± 1623.320  ops/s
FloatsArrayBenchmark.times2  thrpt   20  198764.909 ± 1608.387  ops/s

Java字节码是相同的,这意味着它与一些较低级别的优化有关。有人可以解释为什么会这样吗?这是基准:

import org.openjdk.jmh.annotations.*;

public class FloatsArrayBenchmark {
    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(new String[]{FloatsArrayBenchmark.class.getSimpleName()});
    }

    @Benchmark @Fork(value = 1,warmups = 0)
    public void times(Data data) {
        float[] result = new float[10000];;
        for (int i = 0,j=0; i < 9_999; i++,j++)
            result[j] = data.floats[i] * 10;
    }
    @Benchmark @Fork(value = 1,warmups = 0)
    public void times2(Data data) {
        float[] result = new float[10000];
        for (int i = 0,j=1; i < 9_999; i++,j++)
            result[j] = data.floats[i] * 10;
    }

    @State(Scope.Benchmark)
    public static class Data {
        private final float[] floats = new float[10000];
    }
}

环境:

  • MacOS,尝试使用Java8,Java11,Java14
  • 2,4 GHz四核Intel Core i5

解决方法

在第一个(更快的)版本中,i始终(有效)与j具有相同的值,因此它:

public void times(Data data) {
    float[] result = new float[10000];;
    for (int i=0,j=0; i < 9_999; i++,j++)
        result[j] = data.floats[i] * 10;
}

可以在没有j的情况下重写,效果相同:

public void times(Data data) {
    float[] result = new float[10000];;
    for (int i = 0; i < 9_999; i++)
        result[i] = data.floats[i] * 10;
}

编译器很可能认识到j是多余的,并消除了冗余,导致执行的++操作数量减少了一半,占所有算术操作的1/3。这与时间一致:第二个版本每次迭代花费70%的时间。 70%约为50%,按比例3:2的操作预期的结果。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...