Java 中的冗余赋值与赋值前检查

Redundant assignment vs check before assignment in Java

在长循环面前,冗余赋值相同的值,还是赋值前检查,成本更高(处理器+内存)?

int count = 0;
for(int i=0; i<100_000; i++){
    if (...) {
        count++
        doLogic(count); // logic is strictly related with count
    } else {
      count = 0;  //50.000 redundant assignment
    }
}

VS.

int count = 0;
for(int i=0; i<10_000; i++){
    if (...) {
        count++
        doLogic(count); // logic is strictly related with count
    } else {
        if(count > 0) { // 50.000 checks 
          count = 0;
        }
    }
}

如果 count 出现在不同的对象中(在 Spring 上下文中作为单例注入)并且 increments/check/reset 会像:

config.incrementCount();
config.getCount();
config.resetCount();

对您问题的简短回答是没关系。两种方法的性能大致相同,并且很可能会由 doLogic.

主导

您的首选应该始终是编写简单且惯用的代码,而不是进行过早的优化。


长答案是这取决于(总是如此,不是吗?)。

首先,您并不知道 JIT 会对您的代码进行何种优化。一个平台和 Java 版本的情况可能不适用于另一个平台。您不能依赖任何未明确 保证.

的东西

其次,您知道他们对过早优化的看法。对代码进行基准测试和概要分析总是一个好主意,但即使是基准测试也不是 100% 可靠的。

好的,让我们进行基准测试:

# First case, variable counter
Benchmark                 Mode  Cnt  Score   Error  Units
Benchmark1.testCounter    avgt    8  0.149 ± 0.026  ms/op
Benchmark1.testCounterIf  avgt    8  0.190 ± 0.036  ms/op

# Second case, counter in the wrapper class
Benchmark                      Mode  Cnt  Score   Error  Units
Benchmark1.testCounterClass    avgt    8  0.198 ± 0.025  ms/op
Benchmark1.testCounterClassIf  avgt    8  0.181 ± 0.016  ms/op

Benchmark code

虽然对于简单计数器变量的情况,“if 优化”似乎失败了,但在第二种情况下,差异在误差范围内。

在我的基准测试中,我只使用了一个包含 Counter class 的简单静态字段。查看由 JIT 生成的 ASM,方法调用似乎是内联的。您的情况可能有所不同,因为企业 java 和 Spring 因在幕后进行晦涩的魔法(例如通过代理和字节码操作)而臭名昭著。不仅方法可能没有内联,而且一些隐藏的开销可能会意外出现。


P.S。如果您对 JVM 的性能和微优化感兴趣,我建议阅读 Alexey Shipilev's JVM Anatomy Quarks Series.