5

I'm testing with this code:

public class TestNull {
   public void leftComparison(String s) {
       if (s == null);
   }
   public void rightComparison(String s) {
       if (null == s);
   }
}

I compiled it with javac 1.8.0_05, and then inspected the bytecode:

public class TestNull {
  ....
  public void leftComparison(java.lang.String);
    Code:
       0: aload_1       
       1: ifnonnull     4
       4: return        

  public void rightComparison(java.lang.String);
     Code:
       0: aconst_null   
       1: aload_1       
       2: if_acmpne     5
       5: return        
}

Apparently, leftComparison is compiled to push and pop 1 variable on the stack while rightComparison pushes and pops 2. I speculate that leftComparison is therefore slightly more efficient than rightComparison?

I'm wondering why the compiler doesn't rewrite the code of rightComparison? In my opinion, the two comparisons should be semantically equivalent, right?

2
  • 3
    If the compiler were in the business of rewriting your code to something that's simpler but equivalent, it'd generate no bytecode at all, because those methods do nothing. :-) Commented Jul 23, 2014 at 3:08
  • Because there is no ifnots bytecode. javac is very literal in how it translates things. Commented Jul 23, 2014 at 3:23

3 Answers 3

5

The Java bytecode compiler does very little in the way of optimization. The serious optimization work is almost all done by the JIT compiler.

I'm wondering why the compiler doesn't rewrite the code of rightComparison?

Because there's no point in rewriting it. The JIT compiler should be able to deal with both of them, and most likely generates optimal (native) code for both versions. (You can check this if you are interested. There are ways to see the native code generated by the JIT compiler.)

(Also see @codenheim's answer for a more technical explanation.)

In my opinion, the two comparisons should be semantically equivalent, right?

That is correct ... but it doesn't mean that the bytecode compiler is obligated to generate the same bytecode sequences for both versions.


The real lesson here is that the bytecode sequences generated by the bytecode compiler tell you very little about how your code is actually going to perform. Any performance conclusions that you might draw from reading the bytecodes are highly suspect.

Sign up to request clarification or add additional context in comments.

Comments

2

In practice, bytecode amounts to Intermediate Code. There is low ROI for optimizing too early or optimizing twice.

  1. It introduces needless layers of maintenance overhead.
  2. Optimization is best done when we know the target platform. The Java stack based instruction set doesn't lend itself to advanced optimization anyway. It is too simplistic (by design). There is only so much "optimization" we can accomplish by shuffling a few stack-based opcodes around. While it is feasible to do a lot of high-level "no brainer" optimizations in javac, I think pragmatism has won here, and we punt it to the JIT which can make use of a far richer native intruction set and techniques (which vary by platform) that just aren't available in bytecode.

Comments

1

Pretty much because "that's how it's written". Comparisons take into account the left-handed variable and compare it to the right. When you say null == s, you're saying "compare null to this variable" so the code defines a constant of null to compare to. The compiler simply optimizes the other comparison to be a non-null check.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.