2

I want to test which of the two methods executes faster, however depending on which one I run first, that method seems to always run slower. I suspect there is some caching mechanism Eclipse is using that speeds up the execution of the second method.

private static int method1(int n) {
    int result = 0;
    for (int i = 0; i < n + 1; ++i) {
        result += i;
    }
    return result;
}

private static int method2(int n) {
    int result = 0;
    int i = 0;
    while (i < n + 1) {
        result += i++;
    }
    return result;
}

This is the main function I am using to test the difference in times.

long start, end;

start = new Date().getTime();
for (int i = 0; i < 100000; ++i) {
    method1(i);
}
end = new Date().getTime();
System.out.println(end - start); // 47

start = new Date().getTime();
for (int i = 0; i < 100000; ++i) {
    method2(i);
}
end = new Date().getTime();
System.out.println(end - start); // 32
budi
  • 6,351
  • 10
  • 55
  • 80
  • 4
    Writing a micro benchmark in Java requires some more effort. Check out this thread: http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java for some insight into the area. – K Erlandsson Jul 18 '15 at 20:07
  • 1
    Keep in mind, that JVM always needs to warm-up, for some initial executions, the resulting times will not be worth attention. – Michał Szydłowski Jul 18 '15 at 20:09
  • So it sounds like this approach is not recommended, correct? – budi Jul 18 '15 at 20:18

3 Answers3

3

Using jmh, I get the following results (for various values of n), score is in nanoseconds per method call (smaller = better):

Benchmark                          (n)  Mode  Samples          Score         Error  Units
c.a.p.SO31495089.method1             1  avgt       10          2.149        0.027  ns/op
c.a.p.SO31495089.method1        100000  avgt       10      34626.763      441.915  ns/op
c.a.p.SO31495089.method1    1000000000  avgt       10  322506247.405  6774340.047  ns/op
c.a.p.SO31495089.method2             1  avgt       10          2.159        0.028  ns/op
c.a.p.SO31495089.method2        100000  avgt       10      34581.273      571.416  ns/op
c.a.p.SO31495089.method2    1000000000  avgt       10  320011679.005  4049907.844  ns/op

Second run:

Benchmark                          (n)  Mode  Samples          Score         Error  Units
c.a.p.SO31495089.method1             1  avgt       10          2.164        0.029  ns/op
c.a.p.SO31495089.method1        100000  avgt       10      34706.194      365.189  ns/op
c.a.p.SO31495089.method1    1000000000  avgt       10  320269697.300  1696038.683  ns/op
c.a.p.SO31495089.method2             1  avgt       10          2.160        0.040  ns/op
c.a.p.SO31495089.method2        100000  avgt       10      34627.163      325.981  ns/op
c.a.p.SO31495089.method2    1000000000  avgt       10  320698252.840  2332275.430  ns/op

Bottom line: the two methods perform similarly, as expected (the difference in execution time is smaller than the measurement error).

assylias
  • 321,522
  • 82
  • 660
  • 783
2

Using new Date().getTime() gives the wall clock time. But it is really not what we as developers want (most of the time, until doing some benchmarking where enterprise level benchmarking are used) because wall clock time is effected by many back ground processes, so to counter that Java provides more sophisticated API for measuring the time.

To exclude the effects of other system activity, you need to measure application "User time" instead.

  • "User time" is the time spent running your application's own code.
  • "CPU time" is user time plus system time. It's the total time spent using a CPU for your application.

Below example to demonstrate CPU and User time calculation using ManagementFactory.getThreadMXBean() API.

    Thread thread = new Thread(){
            public void run() {
                for (int i = 0; i < 100000; ++i) {
                    int result = 0;
                    for (int j = 0; j < i + 1; ++j) {
                        result += j;
                    }
                }
                System.out.println("FOR approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d);
                System.out.println("FOR approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d);
            };
        };
        thread.start();

        Thread thread2 = new Thread(){
            public void run() {
                for (int i = 0; i < 100000; ++i) {
                    int result = 0;
                    int j = 0;
                    while (j < i + 1) {
                        result += j++;
                    }
                }
                System.out.println("WHILE approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d);
                System.out.println("WHILE approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d);
            };
        };
        thread2.start();


Having said, I am really not sure why you are getting unexpected behavior, I ran your code both in Eclipse and IntelliJ IDE, and I always got FOR loop approach as faster than WHILE loop.

Probably try to restart you Eclipse and have lesser number of background processes running OR do not run it Eclipse but run the tests from Java command line so that you can sure of results.

As can be seen from below Byte Code Analysis that WHILE and FOR loop approach has same bytes codes generated, which means there would be same assemble code and hence same time CPU will take to execute instructions.

But practically when we run in your IDE or other wise, then is being effect by background processes, so different times are observed. But in this particular case - WHILE v/s FOR, it is more appropriate to do byte code analysis and conclude that WHILE and FOR loop approaches will take same time.


Byte code of FOR loop:

{
  public Test2();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: bipush        10
         5: if_icmpge     21
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_1
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: iinc          1, 1
        18: goto          2
        21: return
      LineNumberTable:
        line 3: 0
        line 4: 8
        line 3: 15
        line 6: 21
      StackMapTable: number_of_entries = 2
           frame_type = 252 /* append */
             offset_delta = 2
        locals = [ int ]
           frame_type = 250 /* chop */
          offset_delta = 18

}

Byte code of WHILE loop:

{
  public Test();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: bipush        10
         5: if_icmpge     21
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_1
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: iinc          1, 1
        18: goto          2
        21: return
      LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 8
        line 6: 15
        line 8: 21
      StackMapTable: number_of_entries = 2
           frame_type = 252 /* append */
             offset_delta = 2
        locals = [ int ]
           frame_type = 18 /* same */

}

Further reading:

hagrawal7777
  • 14,103
  • 5
  • 40
  • 70
  • "*Theoretically it may be argued that both approach should yield same result, but practically it doesn't*" => I very much doubt that they are different... – assylias Jul 18 '15 at 21:15
  • @assylias I ran results and they turned out to be different times. Even the OP is getting different results. – hagrawal7777 Jul 18 '15 at 21:23
  • `Date` API which means clock time (this is not I am recommending), then CPU and then User time using `ManagementFactory.getThreadMXBean()`, I mentioned the code in my answer. – hagrawal7777 Jul 18 '15 at 21:26
  • Did you give a try (probably use same code as OP so that we all are on same page), if so what were results at your end? – hagrawal7777 Jul 18 '15 at 21:27
  • OK but did you allow for warmup? Did you make sure you did not favour inlining the first method under test? Did you run many tests and took an average or just the result of one test? etc... – assylias Jul 18 '15 at 21:28
  • I was really not doing performance testing, I was just helping OP getting to the next level, if I had to do some performance testing I would have gone for some more sophisticated java profiling and performance measuring tools. Did you give a try at your end, using the code of OP? – hagrawal7777 Jul 18 '15 at 21:36
  • Understood. Yes, see my answer. – assylias Jul 18 '15 at 21:42
  • Dude, you tell us that both approaches generate the same byte code. Then you insist that you got different times when you tested. So you've demonstrated two things: the OP's expectation is correct, there should be no difference in timing between the two approaches. And you've managed to write a test that is unreliable and signals a difference when no difference exists. :-( – Erick G. Hagstrom Jul 19 '15 at 00:19
  • @ErickG.Hagstrom Buddy, check OP's first line "*I want to test which of the two methods executes faster*". This no way suggests that he is expecting both to run with no time difference. Now read my first line "*Using new Date().getTime() gives the wall clock time.*", I am suggesting in my code better way approach to follow than wall clock timing. I am telling that I got different results under normal testing scenario, which is a fact, run the code OP has given and I am sure you will also find time difference. Then read my last para, where I am clearly mentioning why tests in IDE has difference – hagrawal7777 Jul 19 '15 at 01:04
  • I have clearly mentioned that if you run a code which is based on wall clock timing then there would be a difference in time in FOR v/s WHILE, and also I have clearly mentioned how to counter it and a brief convincing explanation that why and how both approaches will have same performance. If you are not using benchmarking API's or some profiler than you will get time difference, and this is a fact, and I have explained that reason as well. Information and explanation I have provided is not way incorrect. Probably you misunderstood it, the way you misunderstood that OP is expecting same time. – hagrawal7777 Jul 19 '15 at 01:06
  • @hagrawal Thanks for emphasizing your intent to get the OP off wall clock time. That's a good thing. OP clearly states he is expecting same time in his comment to frankelydiaz's answer. – Erick G. Hagstrom Jul 19 '15 at 01:17
  • @ErickG.Hagstrom Ah, now I see buddy, but that was not part of his question and I didn't see his reply. – hagrawal7777 Jul 19 '15 at 08:29
  • @budi Did you try running your code from Java command line (or may be restart Eclipse) or still having that issue - "*however depending on which one I run first, that method seems to always run slower. *"? In real time, from IDE or even from Java command line you will see the difference in time of FOR v/s WHILE, but in theory there is no difference in performance of these 2 as I explained through java byte code analysis. – hagrawal7777 Jul 19 '15 at 08:44
0

While and For Loop have a similar performance.

Maybe this similar post would help you Java For Loop Vs While Loop Performance Difference

Community
  • 1
  • 1
Frankely Diaz
  • 886
  • 1
  • 9
  • 16
  • Oh yes, I should have mentioned earlier, I chose to test a for and while loop because I knew the execution times should be exactly/about the same. I was more curious about why there is a substantial difference in the two execution times. – budi Jul 18 '15 at 20:12