6

Will the Java Compiler optimize simple repeated math operations like:

if (prevX / width != curX / width) {
    // Do something with prevX / width value
} else {
    // Do something with curX / width value
}

I know I can just assign the results to a variables before the if statement, and return the variables, but it's kind of cumbersome. If the compiler automatically recognizes that the same calculations are being made and caches the results to temporary variables on its own, I'd rather stick to the above convention.

*Edit - I'm an idiot. I tried to simply/abstract my question too much. It's not at simple as: if (x > y)

mnemy
  • 669
  • 1
  • 9
  • 19
  • 1
    unless width can be negative, can't you just go `if (x > y) ...` – user949300 Jan 20 '12 at 23:56
  • 1
    Why not just `Math.max(x / width, y / width)`? Also @user949300: if these are ints, it could give a different result. – Eric Andres Jan 20 '12 at 23:57
  • @Eric Andres I don't think ints would matter in the original problem presented, since it was only a 2 way check (it didn't check for equality) If (x > y), there is no way that x/width is < y/width assuming width > 0. But ints would matter a lot in the new question, so +1 to you. – user949300 Jan 21 '12 at 00:32

3 Answers3

10

The answer is yes. This is called Common Subexpression Elimination and is a standard (and powerful) compiler optimization used in Java, C/C++ and others...

This page confirms that the HotSpot JVM will do this optimization.


That said, whether or not the compiler/run-time will be able to do this optimization when you expect it to is another story. So I usually prefer to do these optimizations myself if it also enhances readability.

double xw = x / width;
double yw = y / width;

if (xw > yw) {
    return xw;
} else {
    return yw;
}
Jason Pyeron
  • 2,388
  • 1
  • 22
  • 31
Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 1
    +1: The `javac` compiler does next to no optimisations, however the JIT native compiler is highly likely to do this assuming it compiles the code at all (most code doesn't get compiled to native because its not run enough) – Peter Lawrey Jan 21 '12 at 08:51
  • The linked page is from a larger paper: https://web.archive.org/web/20110622085909/http://java.sun.com/products/hotspot/docs/whitepaper/Java_Hotspot_v1.4.1/JHS_141_WP_d2a.pdf – Jason Pyeron Jan 24 '20 at 02:33
3

The compiler may perform such optimizations. Whether it actually does depends on the answers to following:

Is the compiler allowed to do this by the JLS?

In some cases it is not. For instance if prevX was a volatile instance variable, then it must be fetched from memory each time the source code says it is used. Another case is where the common subexpression involves a method call with an observable side-effect; i.e. where something else in program might be able to tell if the method is called once or twice.

Is the compiler capable of doing this?

A compiler needs to analyze the code to detect common subexpressions that could legally be optimized. There two issues here:

  • Is the compiler capable of performing the necessary reasoning? For instance, one could hypothesize a compiler that can determine that a specific method call will be side-effect free and that therefore can be optimized. However, building a compiler that is actually capable of doing this is ... and interesting problem.

  • Is the optimization worthwhile? There is a trade-off between the cost of performing an optimization and the benefits. It is not a straight forward trade-off. It needs to take into account the cost of looking to see if an optimization can be performed ... when it actually can't. In other words, the impact on compilation time. (Bear and mind that in Java the optimizations are mostly done at runtime by the JIT compiler ... so this impacts on application performance.)

In a simple example like yours, the optimization is legal (modulo volatile) and one should expect a half-decent JIT compiler to perform it.


The other question is whether you should try to help the compiler by evaluating the common expressions explicitly your code and assigning the results to temporaries.

IMO, the answer is generally no.

  • A good compiler will probably do as good as job as you at this. And if it doesn't, then the next generation may do.

  • The code probably doesn't warrant hand optimization. Unless you've profiled your code to determine where the bottlenecks are, your hand optimizations stand a good chance of being irrelevant to actual application performance ... and a waste of your time.

  • There is a chance that you will stuff it up; e.g. by forgetting that a method call has an important side-effect or that a variable is volatile for a good reason.

On the other hand, if the rewrite makes your code more readable, that's a good reason to do it.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

In general, "yes" - the compiler will optimize the code if it can, and the HotSpot JVM can also improve repeatedly-executed code blocks.

In this case however, you would be better to refactor the code like this:

if (x > y)
    return x / width;
return y / width;

which avoids one division operation if x > y.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 1
    I just want to add that relying on compiler optimizations and other undocumented features is not a good programming practice. Whether the results are the same or not, writing efficient code always enhances code quality. – buc Jan 21 '12 at 00:00
  • 1
    @buc 99% of the code you write isn't performance sensitive and more often than not the most efficient code isn't the most maintainable code. Especially with how good compilers are these days worrying whether they will do CSE on a simple math function such as this is useless. Just write the clearer code.. – Voo Jan 21 '12 at 00:05