0

Which of the following code snippets (A and B below) is more efficient (performance wise) and why?

Furthermore:

  • Does the compiler rearrange the validate() method calls so that they are called "when needed" i.e. when evaluating each separate condition in the if-statement? In other words, will the compiler treat snippet A just as if it were written as snippet B?

  • Are there other alternatives (besides snippet A and B) for better perfomance?

(validate(...) checks if a field (provided as an argument) contains valid values)

Snippet A:

    boolean okTitle = validate(inputTitle);
    boolean okLocation = validate(inputLocation);
    boolean okDuration = validate(inputDuration);
    boolean okReminder = validate(inputReminder);
    boolean okRepetition = validate(inputRepetition);
    boolean okDate = validate(inputDate);

    if(okTitle && (okLocation || okDuration || okReminder || okRepetition || okDate)){
        setEnabled(true);
    }else{
        setEnabled(false);
    }

Snippet B:

setEnabled(
     validate(inputTitle) && 
     ( validate(inputLocation) || validate(inputDuration) || validate(inputReminder) || validate(inputRepetition) || validate(inputDate) )
   );
ledwinder96
  • 241
  • 5
  • 13

3 Answers3

2

In those situations, very often, the input is valid 95% of the time and/or the validate method is fast: if that's the case, either method will work fine.

If the input is often invalid and/or if the validate method is slow and you call that piece of code often, then you probably want to use the second method and make sure you test the likeliest reason for invalidity first, for a quick exit of the condition code.

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

Not related to the compiler, how the code will be executed depend on the values of input at runtime.

Snippet B will certainly be better performance-wise than snippet A, because A will execute all validate() calls but B will stop when the conditions are satisfied.

When I was younger I would definitely prefer B over A because of the performance advantage. Now, if the validate() method is not really "expensive" to call, I would definitely choose A, because it is much more readable and easier to maintain than B.

Antony Ng
  • 767
  • 8
  • 16
1

will the compiler treat snippet A just as if it were written as snippet B

No, they are not equivalent code due to short circuiting. If you had the validate(...) calls inside the if statement, then it would be equivalent and could compare the bytecode to see if the compiler thinks the same way


Testing both cases using javac Test.java && java Test && javap -c Test

Without if

public class Test
{
    public static void main(String[] args)
    {
        int a = 5;
        System.out.println(a > 2 && (a < 6 || a < 4));
    }
}

Prints true, and bytecode:

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

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5
       1: istore_1
       2: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       5: iload_1
       6: iconst_2
       7: if_icmple     25
      10: iload_1
      11: bipush        6
      13: if_icmplt     21
      16: iload_1
      17: iconst_4
      18: if_icmpge     25
      21: iconst_1
      22: goto          26
      25: iconst_0
      26: invokevirtual #3                  // Method java/io/PrintStream.println:(Z)V
      29: return
}

With if

public class Test
{
    public static void main(String[] args)
    {
        int a = 5;
        if(a > 2 && (a < 6 || a < 4))
            System.out.println(true);
        else
            System.out.println(false);
    }
}

Prints true as expected, and the bytecode:

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

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5
       1: istore_1
       2: iload_1
       3: iconst_2
       4: if_icmple     28
       7: iload_1
       8: bipush        6
      10: if_icmplt     18
      13: iload_1
      14: iconst_4
      15: if_icmpge     28
      18: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      21: iconst_1
      22: invokevirtual #3                  // Method java/io/PrintStream.println:(Z)V
      25: goto          35
      28: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: iconst_0
      32: invokevirtual #3                  // Method java/io/PrintStream.println:(Z)V
      35: return
}

So even with identical logic flow, it generates less bytecode to avoid the if statement

(for this compiler at any rate, using Oracle's jdk8)

phflack
  • 2,729
  • 1
  • 9
  • 21
  • Thank you very much for a detailed answer! However, the only variable you check is `int a`, but in my example I have several more variables. What your answer leaves out is clarity regarding whether it's smarter/faster to evaluate each condition "inside" the if-statement or beforehand. In other words if you had written something along the lines of `boolean a2 = a>2; boolean a6 = a <6; boolean a4= a<4;` and then checked `if(a2 && (a6 || a4)`. I'm going to check myself right after I post this comment and if I'm not confused I'll pick your answer ;) ! – ledwinder96 Dec 07 '17 at 09:50
  • @ledwinder96 I address that in the first paragraph about short-circuiting, pre-computing the booleans is different logical flow than computing them inside the if, I can add a bit to pre-computing in both, but it shouldn't differ much from what's already there – phflack Dec 07 '17 at 13:25