2

Recently I did a incomplete nonogram solver. The solver is based in the iterative line solving approach. Some code is like :

Method 1:

for(; j<i; j++){
     if(a[j] == 0) break;
}

Method 2:

for(; j<i && a[j]!=0; j++);

This piece of code occurs many times in my program.

I think both method 1 and method 2 did the same thing. But when I use time nonogram in a linux machine, when the size of the nonogram is around 5 x 10, then the time is very close to each other, most of time the method 2 faster. When the size is 30x40, I run the program for about 10 times, and most of time the method 1 runs faster then the method 2 (time differences is around 0.2s). Is it a random issue? If it is a just random issue, I will use method 2 over method 1. Are they really different in compiler implementation? Thank you.

Robin
  • 134
  • 9

1 Answers1

10

I've made two basic code tests for this and looked at the assembly code. This is what I got.

Method 1:

public class TestBreak {
    public static void main(String[] args) {
        int i = 10;
        int j = 5;
        int a[] = {5, 10, 6, 90, 0, 1, 0, 7, 10};
        for(; j<i; j++){
            if(a[j] == 0) break;
        }
    }
}

Method 2:

public class TestBreak2 {
    public static void main(String[] args) {
        int i = 10;
        int j = 5;
        int a[] = {5, 10, 6, 90, 0, 1, 0, 7, 10};
        for(; j<i && a[j] != 0; j++){
        }
    }
}

Assembly code for method 1:

public class TestBreak {
  public TestBreak();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1      
       3: iconst_5      
       4: istore_2      
       5: bipush        9
       7: newarray       int
       9: dup           
      10: iconst_0      
      11: iconst_5      
      12: iastore       
      13: dup           
      14: iconst_1      
      15: bipush        10
      17: iastore       
      18: dup           
      19: iconst_2      
      20: bipush        6
      22: iastore       
      23: dup           
      24: iconst_3      
      25: bipush        90
      27: iastore       
      28: dup           
      29: iconst_4      
      30: iconst_0      
      31: iastore       
      32: dup           
      33: iconst_5      
      34: iconst_1      
      35: iastore       
      36: dup           
      37: bipush        6
      39: iconst_0      
      40: iastore       
      41: dup           
      42: bipush        7
      44: bipush        7
      46: iastore       
      47: dup           
      48: bipush        8
      50: bipush        10
      52: iastore       
      53: astore_3      
      54: iload_2       
      55: iload_1       
      56: if_icmpge     74
      59: aload_3       
      60: iload_2       
      61: iaload        
      62: ifne          68
      65: goto          74
      68: iinc          2, 1
      71: goto          54
      74: return        
}

Assembly code for method 2:

public class TestBreak2 {
  public TestBreak2();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1      
       3: iconst_5      
       4: istore_2      
       5: bipush        9
       7: newarray       int
       9: dup           
      10: iconst_0      
      11: iconst_5      
      12: iastore       
      13: dup           
      14: iconst_1      
      15: bipush        10
      17: iastore       
      18: dup           
      19: iconst_2      
      20: bipush        6
      22: iastore       
      23: dup           
      24: iconst_3      
      25: bipush        90
      27: iastore       
      28: dup           
      29: iconst_4      
      30: iconst_0      
      31: iastore       
      32: dup           
      33: iconst_5      
      34: iconst_1      
      35: iastore       
      36: dup           
      37: bipush        6
      39: iconst_0      
      40: iastore       
      41: dup           
      42: bipush        7
      44: bipush        7
      46: iastore       
      47: dup           
      48: bipush        8
      50: bipush        10
      52: iastore       
      53: astore_3      
      54: iload_2       
      55: iload_1       
      56: if_icmpge     71
      59: aload_3       
      60: iload_2       
      61: iaload        
      62: ifeq          71
      65: iinc          2, 1
      68: goto          54
      71: return        
}

As you can see, the only difference is a single line in assembly code of method 1:

62: ifne          68

Apart of that, they generate similar byte code, so the performance difference may not be really something.

If you really want to measure which one is faster, write a micro benchmark. For that, follow the rules explained here: How do I write a correct micro-benchmark in Java?.

Another piece of info: if you really want to spot performance bottlenecks in your application, use a profiler which will tell you explicitly which method(s) is(are) consuming more cpu than others, then put your effort on solving the performance problems on that(those) method(s) instead of guessing where the problems may be.

About how to generate the byte code, please read about javap command, specifically javap -c.

Community
  • 1
  • 1
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332