16

I was teaching an introductory programming course today and was walking through some simple code involving variable assignments in Java. The point of the code wasn't to show off anything particular exciting, but mostly to make sure students understood variable assignment statements.

I had the following method up on the board and was tracing through it one line at a time:

private void simpleMethod() {
    int myInt = 137;
    myInt = 42;
    myInt = myInt + 1;

    /* ... code using myInt ... */
}

A student asked me whether myInt would ever actually hold the values 137 and 42 when the program ran, or if it would just jump straight to holding 43. I told the student that the code would execute each line in turn, so the variable would actually hold these intermediate values.

Honestly, though, I wasn't sure what bytecode javac would emit (completely ignoring the optimizations done by the JVM). Is javac (or any Java compiler) legally allowed to optimize the silly assignment statements away and to instead just directly initialize myInt to 43?

According to javap, on my system, the above code compiled with javac produces

   0: sipush        137
   3: istore_1      
   4: bipush        42
   6: istore_1      
   7: iload_1       
   8: iconst_1      
   9: iadd          
  10: istore_1      
  11: return        

So there is no optimization going on here. My question, though, is whether it's legal to optimize this or not, so this doesn't resolve anything.

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • I think you can test easily with `javap` to see whether the assignment is optimized. Whether legal or not to optimize this... that's something I also want to know. – nhahtdh Jan 15 '13 at 07:03
  • 1
    @nhahtdh- Output from `javap` shown above. No optimization done. – templatetypedef Jan 15 '13 at 07:05

4 Answers4

8

The JLS specifies only the contract of observable behavior that your program produces. Since myInt is local, the optimization can indeed be optimized at compile time, since this would produce a behavior consistent with the spec, and there's nothing in the spec that says it's not allowed (at least, not that I found!). Chapter 1 of the spec specifies the observable-ness of the spec explicitly: This document fully specifies the (apparent) order of evaluation of expressions.... Since the apparent behavior is unchanged by constant-folding to myInt = 43, the optimization would be consistent with the JLS.

In fact, the compilation target of a Java application isn't even specified in the JLS. Chapter 1 says that Java applications "normally" compile to the bytecode specified in the JVM spec (a separate document), but it does not require that they do so. There are some statements that must be optimized at compile time, but myInt is not one such. Even if myInt were a field, I think the optimization would be allowed; the different behavior would still be valid behavior, even if myInt is volatile (since it represents one valid ordering of events).

So, short answer, I think your student is correct; it's perfectly fine to optimize it to just myInt = 43. That said, javac generally does very little -- virtually nothing -- in the way of optimization. Optimizations are pretty much all done in the JIT.

yshavit
  • 42,327
  • 7
  • 87
  • 124
5

I believe the Java compiler is allowed to do any constant folding that it can statically determine at compile time.

So yes, this could be optimised by javac.

However javac does not need to make this optimisation because the JVM JIT compiler will almost certainly do the same optimisation for you a bit further down the line. From that point of view, whether or not the Java -> Bytecode compiler does this optimisation is probably irrelevant in terms of impact on the actual native code executed at runtime.

mikera
  • 105,238
  • 25
  • 256
  • 415
  • 1
    The only safe case that this can be done at compile time is probably local variable. If the code involves instance or class member, compile time optimization may result in unexpected behavior. – nhahtdh Jan 15 '13 at 07:13
  • 1
    Can you find a reference backing this up? I'm sorry if it seems like an unusual request, but I've been wondering about this sort of question for a while and would like a definitive answer if possible. – templatetypedef Jan 15 '13 at 07:16
  • I'm looking for a reference in the JLS. Can't seem to find it though. Maybe it's a case of anything not in contradiction with the JLS is allowed..... – mikera Jan 15 '13 at 07:20
0

So there is no optimization going on here.

This shouldn't be too surprising given Java uses dynamic compilation.

The JVM optimises almost all the code at runtime, this means that no matter whether you use code compiled for Java 1.0 in 1996 or Scala or JRuby, JGo etc, you get the full benefit of the optimisation to native code of your particular CPU model.

For this reason many languages have JVM implementations so they don't need to be able to produce optimal code for all the platforms the JVM runs on.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Although this is true, my question is whether the compiler is legally permitted to do this optimization, rather than whether or not it's a good idea or the right place to do the optimization. Thanks, though! – templatetypedef Jan 15 '13 at 08:25
  • @templatetypedef Given the last 16 years of compiler designed being to make it as simple as possible in terms of optimisation, I believe it is not something the `javac` will ever do. The compiler is likely to avoid doing any optimisation which can be easily done by a sane developer. Note: The JIT is unlikely to optimise code unless it has been run 10,000 times. In short, while in theory the optimisation is legal it is unlikely to ever happen in practice. – Peter Lawrey Jan 15 '13 at 08:42
0

Although this is true, my question is whether the compiler is legally permitted to do this optimization

By traditional data flow analysis (flow/transfer functions, in-sets, out-sets, gen-sets, kill-sets, etc.), specifically some-paths forward data flow, a.k.a. reaching definitions (or use-def chains)... yes. Essentially, the optimizer can link each use of a variable to the definitions that reach it.

In this case, an optimizer could determine that the initial definition of myInt (=137) never reaches its use, while the other definition (=42 1) does reach its use since it is never redefined on any (some) path from that definition to its use.

References:

  • Dragon book 1ed (1986) chapter 10
  • Dragon book 2ed (2007) chapter 9 (9.2 specifically)

1 - I use 42 all the time in my teaching code. I bet we all do.

Alan
  • 3,815
  • 1
  • 26
  • 35