可能重复:
这些代码中哪一段在 Java 中更快?
如果我写一个循环为
for (int i=n; i>=0; i--)
而另一个作为
for (int i=0; i<=n; i++)
在java中哪个会更快,为什么?..说n = 10000
可能重复:
这些代码中哪一段在 Java 中更快?
如果我写一个循环为
for (int i=n; i>=0; i--)
而另一个作为
for (int i=0; i<=n; i++)
在java中哪个会更快,为什么?..说n = 10000
永远不要怀疑;用谷歌卡尺找出来。由于围绕零与上限以及递增与递减测试的相对权重进行了相当多的讨论,以下是所有这些情况的笛卡尔积:
import java.util.Random;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;
public class Performance extends SimpleBenchmark {
static final Random rnd = new Random();
public int timeDecrementToZero(int reps) {
int sum = rnd.nextInt();
for (int i = 0; i < reps; i++) {
for (int j = Integer.MAX_VALUE; j >= 0; j--) sum += j;
}
return sum;
}
public int timeDecrementFromZero(int reps) {
int sum = rnd.nextInt();
for (int i = 0; i < reps; i++) {
for (int j = 0; j > Integer.MIN_VALUE; j--) sum += j;
}
return sum;
}
public int timeIncrementFromZero(int reps) {
int sum = rnd.nextInt();
for (int i = 0; i < reps; i++) {
for (int j = 0; j < Integer.MAX_VALUE; j++) sum += j;
}
return sum;
}
public int timeIncrementToZero(int reps) {
int sum = rnd.nextInt();
for (int i = 0; i < reps; i++) {
for (int j = Integer.MIN_VALUE; j < 0; j++) sum += j;
}
return sum;
}
public static void main(String... args) {
Runner.main(Performance.class, args);
}
}
结果:
0% Scenario{vm=java, trial=0, benchmark=DecrementToZero} 984060500.00 ns; σ=30872487.22 ns @ 10 trials
25% Scenario{vm=java, trial=0, benchmark=DecrementFromZero} 982646000.00 ns; σ=35524893.00 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=IncrementFromZero} 1023745500.00 ns; σ=24828496.82 ns @ 10 trials
75% Scenario{vm=java, trial=0, benchmark=IncrementToZero} 1081112500.00 ns; σ=20160821.13 ns @ 10 trials
benchmark ms linear runtime
DecrementToZero 984 ===========================
DecrementFromZero 983 ===========================
IncrementFromZero 1024 ============================
IncrementToZero 1081 ==============================
显然,限制是否为零比使用 inc 与 dec 的影响要小。
为了指出这些差异有多么微妙,这里几乎是相同的代码,但现在它使用long
s (我包括第一个示例中的一个方法,以保持规模):
public int timeDecrementFromZeroInt(int reps) {
int sum = rnd.nextInt();
for (int i = 0; i < reps; i++) {
for (int j = 0; j > Integer.MIN_VALUE; j--) sum += j;
}
return sum;
}
public long timeDecrementFromZero(int reps) {
long sum = rnd.nextLong();
for (long i = 0; i < reps; i++) {
for (long j = 0; j > Integer.MIN_VALUE; j--) sum += j;
}
return sum;
}
public long timeIncrementFromZero(int reps) {
long sum = rnd.nextLong();
for (long i = 0; i < reps; i++) {
for (long j = 0; j < Integer.MAX_VALUE; j++) sum += j;
}
return sum;
}
public long timeDecrementToZero(int reps) {
long sum = rnd.nextLong();
for (long i = 0; i < reps; i++) {
for (long j = Integer.MAX_VALUE; j >= 0; j--) sum += j;
}
return sum;
}
public long timeIncrementToZero(int reps) {
long sum = rnd.nextLong();
for (long i = 0; i < reps; i++) {
for (long j = Integer.MIN_VALUE; j < 0; j++) sum += j;
}
return sum;
}
结果:
0% Scenario{vm=java, trial=0, benchmark=DecrementFromZeroInt} 978513000.00 ns; σ=14861284.82 ns @ 10 trials
20% Scenario{vm=java, trial=0, benchmark=DecrementFromZero} 2160652000.00 ns; σ=13825686.87 ns @ 3 trials
40% Scenario{vm=java, trial=0, benchmark=IncrementFromZero} 2153370000.00 ns; σ=6318160.49 ns @ 3 trials
60% Scenario{vm=java, trial=0, benchmark=DecrementToZero} 4379893000.00 ns; σ=8739917.79 ns @ 3 trials
80% Scenario{vm=java, trial=0, benchmark=IncrementToZero} 4383569000.00 ns; σ=5798095.89 ns @ 3 trials
benchmark ms linear runtime
DecrementFromZeroInt 979 ======
DecrementFromZero 2161 ==============
IncrementFromZero 2153 ==============
DecrementToZero 4380 =============================
IncrementToZero 4384 ==============================
主要结论:永远不要在如此低的水平上对性能做出任何假设。编写完整的代码并对其进行整体测试,因为总会有一些你没有考虑到的东西,这完全扭转了局面。
CPU 可能有一种更快的方法来比较一个数字 (i) 和 0,而不是比较另一个任意数字 (n)。从理论上讲,这将使递减版本更快。
恕我直言,这纯粹是学术性的。它们基本上都是“相同的”,所以你应该实现一个对你之后维护你的代码的人来说更合乎逻辑和更容易理解的那个。
只需按照最有意义的方式编写循环即可。您不太可能 (a) 做任何对时间至关重要的事情,以至于在整个程序持续时间内多花几纳秒就会产生影响,并且 (b) 您的代码经过优化,瓶颈是一个中的增量或减量操作环形。
如果在您测试之后,分析显示特定循环是一个问题,那么请担心优化该循环,关注循环体而不是诸如增量和减量之类的事情。
答案是这取决于是什么n
。倒计时时,代码只需访问n
一次。数数时,这可能不是真的。因此,例如,如果n
是一个volatile
字段,或者如果循环体中的某些内容可能会改变 的值n
,则每次循环时都需要查找该值。这将显着减慢循环速度。
使用此代码,向上计数比向下计数慢数百倍:
public class Counts {
private static final int ITERS = 100000;
volatile int n = 1000;
public long countUp() {
long start = System.nanoTime();
for (int iter = 0; iter < ITERS; ++iter) {
for (int i = 0; i < n; ++i) {
// do nothing
}
}
return System.nanoTime() - start;
}
public long countDown() {
long start = System.nanoTime();
for (int iter = 0; iter < ITERS; ++iter) {
for (int i = n - 1; i >= 0; --i) {
// do nothing
}
}
return System.nanoTime() - start;
}
}
如果有任何可测量的差异,则与 0 比较的 Variant 更快,因为在 CPU 级别上,与 0 的比较更快。但是在大多数情况下,您最好使用可读性好的代码
在大多数情况下,与其他人了解正在发生的事情所需的时间相比,时间上的差异是微不足道的。只需使用最容易遵循的方法。如果你想测试它,你可以运行这样的东西:
long startTime = System.nanoTime();
long duration, endTime;
for (int i=0;i<1000 ;i++ ) {
//function
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.printf("The duration was %d.%03d microseconds%n", duration / 1000, duration % 1000);
对于增量和减量。
更大的问题是前后递增/递减。从第 2 页底部开始有一个很好的解释:http ://www.iar.com/Global/Resources/Developers_Toolbox/C_Cplusplus_Programming/Writing%20optimizer-friendly%20code.pdf