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
虽然对于简单计数器变量的情况,“if 优化”似乎失败了,但在第二种情况下,差异在误差范围内。
在我的基准测试中,我只使用了一个包含 Counter
class 的简单静态字段。查看由 JIT 生成的 ASM,方法调用似乎是内联的。您的情况可能有所不同,因为企业 java 和 Spring 因在幕后进行晦涩的魔法(例如通过代理和字节码操作)而臭名昭著。不仅方法可能没有内联,而且一些隐藏的开销可能会意外出现。
P.S。如果您对 JVM 的性能和微优化感兴趣,我建议阅读 Alexey Shipilev's JVM Anatomy Quarks Series.
在长循环面前,冗余赋值相同的值,还是赋值前检查,成本更高(处理器+内存)?
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
虽然对于简单计数器变量的情况,“if 优化”似乎失败了,但在第二种情况下,差异在误差范围内。
在我的基准测试中,我只使用了一个包含 Counter
class 的简单静态字段。查看由 JIT 生成的 ASM,方法调用似乎是内联的。您的情况可能有所不同,因为企业 java 和 Spring 因在幕后进行晦涩的魔法(例如通过代理和字节码操作)而臭名昭著。不仅方法可能没有内联,而且一些隐藏的开销可能会意外出现。
P.S。如果您对 JVM 的性能和微优化感兴趣,我建议阅读 Alexey Shipilev's JVM Anatomy Quarks Series.