简体   繁体   中英

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:)

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.

The most-obvious solution to this problem is simply to rewrite the code something like this:

if (someList.contains(id)) {
  if (this.checkIfDisabled(id)) {
    // do something here

If, in your human estimation of the problem, one test is likely to mean that the other test does not need to be performed at all, then simply "write it that way."

Java compiler optimizations are tricky. Most optimizations are done at runtime by the JIT compiler. There are several levels of optimizations, the maximum number of optimizations by default will be made after 5000 method invocations. But it is rather problematic to see which optimizations are applied, since JIT compile the code directly into the platform's native code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM