javaperformancefor-loopmicrobenchmark

Java for loop performance question


considering this example:

public static void main(final String[] args) {
    final List<String> myList = Arrays.asList("A", "B", "C", "D");
    final long start = System.currentTimeMillis();
    for (int i = 1000000; i > myList.size(); i--) {
        System.out.println("Hello");
    }
    final long stop = System.currentTimeMillis();
    System.out.println("Finish: " + (stop - start));
}

vs

public static void main(final String[] args) {
    final List<String> myList = Arrays.asList("A", "B", "C", "D");
    final long start = System.currentTimeMillis();
    final int size = myList.size();
    for (int i = 1000000; i > size; i--) {
        System.out.println("Hello");
    }
    final long stop = System.currentTimeMillis();
    System.out.println("Finish: " + (stop - start));
}

Will this make any diffrence ? On my machine the second one seems to perform faster but i don't know if it is really accurate. Will the compiler optimze this code ? I could think that he would if the loop condition is an immutable object (e.g. String array).


Solution

  • If you want to test something like this, you really must optimize your microbenchmark to measure what you care about.

    First, make the loop inexpensive but impossible to skip. Computing a sum usually does the trick.

    Second, compare the two timings.

    Here's some code that does both:

    import java.util.*;
    
    public class Test {
    
    public static long run1() {
      final List<String> myList = Arrays.asList("A", "B", "C", "D");
      final long start = System.nanoTime();
      int sum = 0;
      for (int i = 1000000000; i > myList.size(); i--) sum += i;
      final long stop = System.nanoTime();
      System.out.println("Finish: " + (stop - start)*1e-9 + " ns/op; sum = " + sum);
      return stop-start;
    }
    
    public static long run2() {
      final List<String> myList = Arrays.asList("A", "B", "C", "D");
      final long start = System.nanoTime();
      int sum = 0;
      int limit = myList.size();
      for (int i = 1000000000; i > limit; i--) sum += i;
      final long stop = System.nanoTime();
      System.out.println("Finish: " + (stop - start)*1e-9 + " ns/op; sum = " + sum);
      return stop-start;
    }
    
    public static void main(String[] args) {
      for (int i=0 ; i<5 ; i++) {
        long t1 = run1();
        long t2 = run2();
        System.out.println("  Speedup = " + (t1-t2)*1e-9 + " ns/op\n");
      }
    }
    
    }
    

    And if we run it, on my system we get:

    Finish: 0.481741256 ns/op; sum = -243309322
    Finish: 0.40228402 ns/op; sum = -243309322
      Speedup = 0.079457236 ns/op
    
    Finish: 0.450627151 ns/op; sum = -243309322
    Finish: 0.43534661700000005 ns/op; sum = -243309322
      Speedup = 0.015280534 ns/op
    
    Finish: 0.47738474700000005 ns/op; sum = -243309322
    Finish: 0.403698331 ns/op; sum = -243309322
      Speedup = 0.073686416 ns/op
    
    Finish: 0.47729349600000004 ns/op; sum = -243309322
    Finish: 0.405540508 ns/op; sum = -243309322
      Speedup = 0.071752988 ns/op
    
    Finish: 0.478979617 ns/op; sum = -243309322
    Finish: 0.36067492700000003 ns/op; sum = -243309322
      Speedup = 0.11830469 ns/op
    

    which means that the overhead of the method call is approximately 0.1 ns. If your loop does things that take no more than 1-2 ns, then you should care about this. Otherwise, don't.