我们说我们必须在循环中执行10000次此计算。
案例1
double answer = i * 1.6712 * 1000 * 60;
案例2
double answer = i * 100272; // 1.6712 * 1000 * 60 = 100272
其中i
是循环索引。
的问题
什么是最有效的方法(就CPU周期而言),案例1或2以及为什么?
答案 0 :(得分:3)
以下是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乘法慢得多,但仍然很有趣。也许是因为类型铸造?