假设我们必须在一个循环中执行此计算 10000 次。
案例一
double answer = i * 1.6712 * 1000 * 60;
案例二
double answer = i * 100272; // 1.6712 * 1000 * 60 = 100272
其中 i
是循环索引。
问题
情况 1 或 2 中最有效的方法是什么(就 CPU 周期而言),为什么?
最佳答案
这是一个 JMH 基准:
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode({ Mode.Throughput })
@Warmup(iterations = 10)
@Fork(value = 1)
@State(Scope.Benchmark)
public class MyBenchmark {
private static final double CONSTANT = 1.6712 * 1000 * 60;
private double x = 0;
@Benchmark
public void testCaseOne() {
for (double i = 1; i < 1000_000; i++) {
x += i * 1.6712 * 1000 * 60;
}
}
@Benchmark
public void testCaseTwo() {
for (double i = 1; i < 1000_000; i++) {
x += i * (1.6712 * 1000 * 60);
}
}
@Benchmark
public void testCaseThree() {
for (double i = 1; i < 1000_000; i++) {
x += i * 100272;
}
}
@Benchmark
public void testCaseFour() {
final double constant = 1.6712 * 1000 * 60;
for (double i = 1; i < 1000_000; i++) {
x += i * constant;
}
}
@Benchmark
public void testCaseFive() {
for (double i = 1; i < 1000_000; i++) {
x += i * CONSTANT;
}
}
}
结果:
Benchmark Mode Cnt Score Error Units
MyBenchmark.testCaseOne thrpt 20 680,452 ± 15,700 ops/s
MyBenchmark.testCaseTwo thrpt 20 721,542 ± 14,131 ops/s
MyBenchmark.testCaseThree thrpt 20 729,411 ± 17,031 ops/s
MyBenchmark.testCaseFour thrpt 20 735,255 ± 16,001 ops/s
MyBenchmark.testCaseFive thrpt 20 719,481 ± 5,338 ops/s
Java 版本:
openjdk version "1.8.0_45-internal"
OpenJDK Runtime Environment (build 1.8.0_45-internal-b14)
OpenJDK 64-Bit Server VM (build 25.45-b02, mixed mode)
正如你所看到的,吞吐量没有显着差异,所以你可以用最清晰易懂的方式来写。
关于我之前的基准测试结果:
Benchmark Mode Cnt Score Error Units
MyBenchmark.testCaseOne thrpt 20 228,285 ± 2,232 ops/s
MyBenchmark.testCaseTwo thrpt 20 593,755 ± 8,165 ops/s
MyBenchmark.testCaseThree thrpt 20 1035,549 ± 20,908 ops/s
之前的基准测试被打破了 - for 循环中的计数器是 int
类型,而在 testCaseThree
中它正在做整数乘法,这就是它快得多的原因.其他结果也受到基准测试中这个错误的影响。
尽管混合整数-双倍乘法比纯 int-int 和 double-double 乘法慢得多,但仍然很有趣。可能是因为类型转换?
关于java - 执行乘法最有效的方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30556137/