Search code examples
javaperformancejvmjavacbytecode

Why does the Java Compiler not inline these calls in a single if clause?


I have been pulled into a performance investigation of a code that is similar to the below:

private void someMethod(String id) {
   boolean isHidden = someList.contains(id);
   boolean isDisabled = this.checkIfDisabled(id);

   if (isHidden && isDisabled) {
     // Do something here
   }
}

When I started investigating it, I was hoping that the compiled version would look like this:

private void someMethod(String id) {
   if (someList.contains(id) && this.checkIfDisabled(id)) {
     // Do something here
   }
}

However, to my surprise, the compiled version looks exactly like the first one, with the local variables, which causes the method in isDisabled to always be called, and that's where the performance problem is in.

My solution was to inline it myself, so the method now short circuits at isHidden, but it left me wondering: Why isn't the Java Compiler smart enough in this case to inline these calls for me? Does it really need to have the local variables in place?

Thank you :)


Solution

  • First: the java compiler (javac) does almost no optimizations, that job is almost entirely done by the JVM itself at runtime.

    Second: optimizations like that can only be done when there is no observable difference in behaviour of the optimized code vs. the un-optimized code.

    Since we don't know (and the compiler presumably also doesn't know) if checkIfDisabled has any observable side-effects, it has to assume that it might. Therefore even when the return value of that method is known to not be needed, the call to the method can't be optimized away.

    There is, however an option for this kind of optimization to be done at runtime: If the body (or bodies, due to polymorphism) of the checkIfDisabled method is simple enough then it's quite possible that the runtime can actually optimize away that code, if it recognizes that the calls never have a side-effect (but I don't know if any JVM actually does this specific kind of optimization).

    But that optimization is only possible at a point where there is definite information about what checkIfDisabled does. And due to the dynamic class-loading nature of Java that basically means it's almost never during compile time.

    Generally speaking, while some minor optimizations could possibly be done during compile time, the range of possible optimizations is much larger at runtime (due to the much increased amount of information about the code available), so the Java designers decided to put basically all optimization effort into the runtime part of the system.