-4

Possible Duplicate:
Which of these pieces of code is faster in Java?

If i write a loop as

for (int i=n; i>=0; i--)

And other one as

for (int i=0; i<=n; i++)

In java which one would be faster and why?..Say n=10000

Community
  • 1
  • 1
Abhishek_Mishra
  • 4,551
  • 4
  • 25
  • 38
  • 1
    Run it and time it yourself. I recall seeing something about decrements being slower than increments, albeit, by small amounts of time. 10 ms or so. – Max Dec 07 '12 at 19:18
  • in terms of performance they are both the same – PermGenError Dec 07 '12 at 19:18
  • http://stackoverflow.com/questions/6646467/how-to-find-time-taken-to-run-java-program – jmj Dec 07 '12 at 19:18
  • Neither. They are the same. Each requires the same work of the ALU. – The111 Dec 07 '12 at 19:18
  • @Abhi_Mishra.. `=<` is not the same as `<=`. First one will not compile. – Rohit Jain Dec 07 '12 at 19:21
  • 1
    Unless the performances of `++` and `--` are different, there's no actual difference. Even if there was, you'll need an extense loop to actually get to notice it in a way that can harm your application's overall performance. – Fritz Dec 07 '12 at 19:22
  • 1
    @Max What do you mean, "10 ms or so"? We're talking nanoseconds here. – Marko Topolnik Dec 07 '12 at 19:24
  • @Max But in my case its decrement which is faster.Why is it so? – Abhishek_Mishra Dec 07 '12 at 19:25
  • This is more than adequately explored here: http://stackoverflow.com/questions/1656506/which-of-these-pieces-of-code-is-faster-in-java – femtoRgon Dec 07 '12 at 19:27
  • This has been asked before http://stackoverflow.com/questions/4181941/loop-counter-in-java-api – Peter Rasmussen Dec 07 '12 at 19:28
  • @MarkoTopolnik Sorry, I should clarify. I'm pretty sure what I read was a loop with thousands of elements. Thus the delay between the two was in ms. – Max Dec 07 '12 at 19:29
  • @Max Thousands of elements turns nanoseconds into microseconds; it would have to be millions of elements. Anyway, the relevant aspect is percentage difference, which on my setup turns out to be over 10% in favor of increment (who knows why, though :) – Marko Topolnik Dec 07 '12 at 19:34
  • 1
    Its Not a Question of inc or dec, its more the compare to 0 Or n – AlexWien Dec 07 '12 at 19:49
  • @AlexWien How do you know that? It's both at the same time. – Marko Topolnik Dec 07 '12 at 20:52
  • @MarkoTopolnik i asume it, because inc and dec have same speed at cpu level, but compare to 0 is faster. But what the VM finally does is another question – AlexWien Dec 07 '12 at 20:55
  • @AlexWien I already had the discussion once about comparison to zero. It turned out that even on the original old 8086, there was no difference at all. But anyway, if you have anything in the way of proof for your thesis, please let me know. – Marko Topolnik Dec 07 '12 at 21:06
  • @MarkoTopolnik Ok, i had never measured it, and will not do in future. i just remembered Z80 and MOS 6502 assembler, ... long time ago ... – AlexWien Dec 08 '12 at 15:54
  • @AlexWien Ahh, the memories :) But the main point from the assembler perspective is skipping a `cmp ax, [n]` because after `dec ax` you can directly use `jnz loop`. – Marko Topolnik Dec 08 '12 at 16:00

6 Answers6

9

Never wonder; use Google Caliper to find out. Since there has been quite a bit of discussion around the relative weights of testing against zero vs. upper limit and incrementing vs. decrementing, here's the Cartesian product of all these cases:

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);
  }
}

Results:

 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 ==============================

Apparently, whether the limit is zero or not has less effect than using inc vs. dec.

Let's change it just a tiny bit...

To point out just how tenouous these differences are, here's virtually the same code, but now it uses longs (I include one method from the first example, to maintain scale):

  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;
  }

Results:

 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 ==============================

Main conclusion: never assume anything about performance at such a low level. Write your full code and test it as a whole because there will always be something else you are not taking into account, which completely turns the tables.

Community
  • 1
  • 1
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Man, this is awesome. But why I can't download it? Is this the correct link to download - http://code.google.com/p/caliper/ ? – Rohit Jain Dec 07 '12 at 19:38
  • There are no artifacts to download, just check out the source code at http://code.google.com/p/caliper/source/checkout. – Marko Topolnik Dec 07 '12 at 19:39
  • I'm not convinced this is a significant result. The introduction of summation in opposite directions, I would suspect, likely has a larger impact on the time taken, and since you are iterating to INTEGER.MAX_VALUE, and summing into an int, you'll be running into integer overflow as well. – femtoRgon Dec 07 '12 at 19:40
  • The Question is Not the decrement vs increment, ist is the compare to zero. You. Oi – AlexWien Dec 07 '12 at 19:42
  • You can't take _everything_ out of the loop, or the JIT will eliminate the whole loop. – Louis Wasserman Dec 07 '12 at 19:42
  • @femtoRgon The summation of any two ints is exactly as complex for the CPU, and a single-cycle operation in all cases. The very fact that overflow can happen is a guarantee that this is true. – Marko Topolnik Dec 07 '12 at 19:46
  • I wish I could vote enough times to give you a reversal badge on this one. – durron597 Dec 07 '12 at 19:54
  • 1
    This is meaningless. Try making `n` a volatile field (or doing something in the loop that prevents the compiler from recognizing that `n` is constant for the duration of the loop) and your results will be quite different. – Ted Hopp Dec 07 '12 at 19:56
  • Note: the numbers are the same within test error. – Peter Lawrey Dec 07 '12 at 19:57
  • @TedHopp What do you mean by `n`? There is no variable named `n` here, and nothing is constant in the loop, except the loop bounds. – Marko Topolnik Dec 07 '12 at 19:58
  • @PeterLawrey What do you mean? Sigma is 4% so the difference is 3 sigma. – Marko Topolnik Dec 07 '12 at 20:00
  • My point is, when we're talking a trivial machine code advantage of a preference in iteration for comparing to 0, and that being somehow translated into a measurable difference when run in the JVM, I think it's certainly pertinent to discuss what ramifications the attempt at measureing it has, before accepting a result to mean something it doesn't. I wouldn't normally worry at any advantage between simple equalities, or whether setting the overflow flag has an impact, or the finer points of the JVM's internal interpretations impact on time for simple ops. But it's the only way to address this. – femtoRgon Dec 07 '12 at 20:04
  • Sorry, I was referring to `n` in OP's original code. You are using `Integer.MAX_VALUE`, which the compiler knows perfectly well is a constant. The difference between counting up to a volatile value versus counting down from it to 0 is huge. – Ted Hopp Dec 07 '12 at 20:05
  • @TedHopp But of course, then you are stacking one volatile read against `Integer.MAX_VALUE` reads of it. We want something that pronounces increment vs. decrement as much as possible. To test your hypothesis that the compiler precomputes the outcome of the loop, the timing should not be influenced by the specific value of the loop bound. This is easily falsifiable. – Marko Topolnik Dec 07 '12 at 20:15
  • @TedHopp I updated the code sample, I think this is a good response to your criticism of the first version. – Marko Topolnik Dec 07 '12 at 20:19
  • You are now starting at 0 and going either up to `limit` or down to `-limit`. That's not the relevant comparison. Try comparing counting up from 0 to limit against going from limit down to 0. – Ted Hopp Dec 07 '12 at 20:22
  • @TedHopp Why? Now it clearly shows that this is about inc vs. dec and not about comparing to zero vs. nonzero. I can add two more that both compare to zero. – Marko Topolnik Dec 07 '12 at 20:25
  • Why is it necessarily about inc vs. dec? OP's code is inc from 0 to `n` vs. dec from `n` to 0. I maintain that if `n` is `volatile` (or otherwise changeable), then going from `n` to 0 is much faster. It's the nature of the limit, not inc vs. dec, that's the important issue. – Ted Hopp Dec 07 '12 at 20:29
  • I see with your latest version you are using `zero` instead of `0`. That keeps avoiding the true comparison. I challenge you to test **OP's code** with different kinds of declarations for `n`. – Ted Hopp Dec 07 '12 at 20:31
  • @TedHopp What you maintain is a trivially true observation. Testing against a `volatile` limit is clearly and undisputedly slower than testing against a local var/compile-time constant. If you think this question is about that, though, I maintain you are mistaken. – Marko Topolnik Dec 07 '12 at 20:32
  • @femtoRgon I've modified it to start from a random initial value for the sum. This changed the results quite a bit, bringing them closer together. – Marko Topolnik Dec 07 '12 at 23:31
  • @MarkoTopolnik It's a compelling result for comparing increment and decrement operations. Each operation performed in those pairs is a equivalent barring the change of sign. – femtoRgon Dec 08 '12 at 20:59
  • @MarkoTopolnik Having had a chance to do some testing of this myself, Using a summation function much like you have here, I've noticed result that differ from yours, interestingly. I tested inc and dec on ranges Integer.MAX_VALUE/2 (that is, 0 to .5max, .5max to max, -.5max to 0, etc.), I no significant time differences in ints, or favoring loops exitting at 0, but a very clear difference in tests using longs, favoring decrement in both positive and negative ranges. A marked disagreement with your results. Seems this may indicate a significant dependance on runtime environment? – femtoRgon Dec 08 '12 at 22:48
  • @femtoRgon Naturally, yes. The differences we are measuring amount to a single different machine instruction. It surprised me that longs are slower than ints on a 64-bit JVM. I'm studying the disassembled JIT code, maybe I find out something interesting. – Marko Topolnik Dec 09 '12 at 07:25
4

it's possible that the CPU has a faster method of comparing a number (i) against 0 vs. comparing against another arbitrary number (n). This would theoretically make the decrement version faster.

This is purely academic though, IMHO. They're both fundamentally "the same", so you should implement the one which is more logical and understandable to whomever maintains your code after you.

dashrb
  • 952
  • 4
  • 10
2

Just write your loops the way it makes the most sense to write them. It's unlikely you're (a) doing anything so time-critical that a few extra nanoseconds for the entire duration of your program will make a difference and (b) your code is so optimized that the bottleneck is the increment or decrement operations in a loop.

If, after you test, profiling shows a particular loop to be a problem, then worry about optimizing that loop, focusing on the loop body instead of things like increment and decrement.

Nik Bougalis
  • 10,495
  • 1
  • 21
  • 37
2

The answer is that it depends on what n is. When counting down, the code only needs to access n once. When counting up, this may not be true. So, for instance, if n is a volatile field, or if something in the loop body may change the value of n, the value needs to be looked up each time through the loop. This will slow the loop down significantly.

With this code, counting up is several hundred times slower than counting down:

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;
    }
}
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
0

If there is any measurable diffetence, than the Variant by comparing to 0 is faster because on CPU Level a compare to 0 is faster. However in most cases you better stay with a Good readable code

AlexWien
  • 28,470
  • 6
  • 53
  • 83
0

The difference in time is insignificant in most cases compared to the amount of time necessary for someone else to understand what's going on. Just use whatever's easiest to follow. If you wanted to test it you could run something like this:

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); 

for both increment and decrement.

The bigger issue is post- verses pre- increment/decrement. There's a pretty good explanation starting on the bottom of page 2 here: http://www.iar.com/Global/Resources/Developers_Toolbox/C_Cplusplus_Programming/Writing%20optimizer-friendly%20code.pdf

Fennelouski
  • 2,411
  • 1
  • 18
  • 26