当前位置: 首页 > 面试题库 >

“循环取消切换”优化无效

慕容超
2023-03-14
问题内容

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

问题答案:

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;
    }
}

在这种情况下,test1和的性能test2类似:

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


 类似资料:
  • 问题内容: 我使用java for循环进行了一些运行时测试,并发现了一种奇怪的行为。对于我的代码,我需要原始类型(例如int,double等)的包装对象来模拟io和输出参数,但这不是重点。只是看我的代码。具有字段访问权限的对象如何比原始类型更快? 优先类型的循环: 结果: MicroTime原语(最大值:= 10000.0):110 MicroTime原语(最大值:= 100000.0):1081

  • 有时候你会遇到循环,或者递归函数,它们会花费很长的执行时间,可能是你的产品的瓶颈。在你尝试使循环变得快一点之前,花几分钟考虑是否有可能把它整个移除掉,有没有一个不同的算法?你可以在计算时做一些其他的事情吗?如果你不能找到一个方法去绕开它,你可以优化这个循环了。这是很简单的,move stuff out。最后,这不仅需要智慧而且需要理解每一种语句和表达式的开销。这里是一些建议: 删除浮点运算操作。

  • 我有一个任务来优化for循环,以便编译器编译运行更快的代码。目标是使代码在 5 秒或更短的时间内运行,原始运行时间约为 23 秒。原始代码如下所示: 我的第一个想法是在内部for循环上进行循环展开,使它降到5.7秒,循环看起来像这样: 在每个循环的阵列中将其取出12个点后,性能不再增加,所以我的下一个想法是尝试引入一些并行性,所以我做了这个: 这实际上最终减慢了代码的速度,并且每个附加变量再次减慢

  • 问题内容: 我在学校被告知,修改a的index变量是一种不好的做法: 范例: 有论据认为, 某些编译器优化可以优化循环, 而无需重新计算索引并限制每个循环。 我进行了一些测试,似乎默认情况下每次都会重新计算索引和绑定。 我想知道是否有可能在中激活这种功能? 例如,优化这种循环: 无需写: 这只是一个例子,我很想尝试一下改进。 编辑 根据Peter Lawrey的回答, 为什么在这个简单的示例中JV

  • 问题内容: 在写另一个问题的答案时,我注意到用于JIT优化的奇怪边框。 以下程序 不是 “ Microbenchmark”, 也不 旨在可靠地衡量执行时间(如对另一个问题的回答所指出)。它仅用作MCVE来重现此问题: 它基本上运行相同的循环,其中将限制一次设置为,将一次设置为。 当在Win7 / 64上使用JDK 1.7.0_21和 计时结果如下: 显然,对于JIT 的情况,JIT可以完成预期的工

  • 分析程序你经常会看到大部分时间都花费在最内层的循环上面。 提高速度的方法就是认真地用汇编优化最花时间的循环。 其它的部分仍然用高级语言完成。 下面所有的例子都假定数据全在1级cache内。 如果数据cache失效是瓶颈,那么没有理由去对指令进行优化。 而应该把注意力集中在组织你的数据,尽量减少cache失效次数(第七章)。 25.1 PPlain和PMMX上的循环 循环通常包括一个控制叠代次数的计