首页 > 解决方案 > 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 上下文中作为单例注入)并且增量/检查/重置将如下所示,它的成本是否相同:

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

标签: javaalgorithmperformancedesign-patternsperformancecounter

解决方案


对您的问题的简短回答是没关系。任何一种方法都将具有大致相同的性能,并且很可能会被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

基准代码

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

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


PS 如果您对 JVM 的性能和微优化感兴趣,我建议您阅读Alexey Shipilev 的 JVM Anatomy Quarks 系列


推荐阅读