Optimization by Java Compiler
Asked Answered
F

6

84

Recently, I was reading this article.

According to that article, Java Compiler i.e. javac does not perform any optimization while generating bytecode. Is it really true? If so, then can it be implemented as an intermediate code generator to remove redundancy and generate optimal code?

Folsom answered 12/5, 2011 at 16:42 Comment(2)
Not exactly a duplicate, but the answer may be interesting: #1680524Weidar
Well, that article is almost ten years old. Is it still true (I don't claim to know either way)? I recommend you do some research using more modern documents.Aristotelianism
A
105

javac will only do a very little optimization, if any.

The point is that the JIT compiler does most of the optimization - and it works best if it has a lot of information, some of which may be lost if javac performed optimization too. If javac performed some sort of loop unrolling, it would be harder for the JIT to do that itself in a general way - and it has more information about which optimizations will actually work, as it knows the target platform.

Analisaanalise answered 12/5, 2011 at 16:44 Comment(8)
-1 for spreading the Java myth that compile-time optimization is undesirable. Some compilation passes can be very expensive, and it's nonsense to spread the idea that the JIT should be responsible for doing all the work. In fact, C and C++ still do a better job than Java when it comes to optimization.Loiret
@Waneck: That's your opinion, but it's clearly not one shared by the Java implementation team. javac does do very little optimization if any. And yes, some optimizations can be expensive, which is why Hotspot does it progressively. And the fact that it does it with more information about the target environment (including what classes are actually loaded) allows it to perform some optimizations which can't be performed statically. There are some situations where AOT optimization will work better, certainly - but the reverse is true too.Analisaanalise
I understand that JIT compilation can perform optimizations that can't be performed at compile-time; However it's no excuse to not perform any optimization at compile-time, and that's the Java myth I was referring to.Loiret
@Waneck: Which exact optimizations do you think should be performed at compile-time then? Bearing in mind that this the output is on a per-class basis, rather than an entire binary. (The differences in terms of execution model, deployment unit etc are very significant here.)Analisaanalise
In my experience, the most effective compiler-based optimizations in Java relate to generic specialization, inlining and avoiding boxing operations. For example, this scala paper lampwww.epfl.ch/~dragos/files/scala-spec.pdf shows that performing specialization of functions led to a 20x speedup, and specialization of arrays has led to 40x. This is an example to show that the JIT isn't the holy grail, and that AOT can go hand-in-hand with JIT.Loiret
Jon, thank you for this really helpful answer. I just found docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/geninfo/…. Are there other resources you would recommend?Genous
@bn.: Not offhand, I'm afraid. Bear in mind that the landscape for performance of JVMs changes all the time - although less so than it used to.Analisaanalise
@Loiret - The link you shared is dead, so I'm not 100% sure what you mean, but I guess it was specialization of generic functions and arrays for primitive values. That's an interesting point, but I think a Java compiler would not be allowed to perform such specializations - it would be against the Java specification. (Maybe a smart JIT can specialize some functions / arrays in some cases? I don't know. The CLR does it - but it was designed that way from the start.)Frederico
R
32

I stopped reading when I got to this section:

More importantly, the javac compiler does not perform simple optimizations like loop unrolling, algebraic simplification, strength reduction, and others. To get these benefits and other simple optimizations, the programmer must perform them on the Java source code and not rely on the javac compiler to perform them.

Firstly, doing loop unrolling on Java source code is hardly ever a good idea. The reason javac doesn't do much in the way of optimization is that it's done by the JIT compiler in the JVM, which can make much better decisions that the compiler could, because it can see exactly which code is getting run the most.

Reddy answered 12/5, 2011 at 16:45 Comment(0)
T
20

The javac compiler once supported an option to generate optimized bytecode by passing -o on the command line.

However starting J2SE1.3, the HotSpot JVM was shipped with the platform, which introduced dynamic techniques such as just-in-time compilation and adaptive optimization of common execution paths. Hence the -o was ignored by the Java compiler starting this version.

I came across this flag when reading about the Ant javac task and its optimize attribute:

Indicates whether source should be compiled with optimization; defaults to off. Note that this flag is just ignored by Sun's javac starting with JDK 1.3 (since compile-time optimization is unnecessary).

The advantages of the HotSpot JVM's dynamic optimizations over compile-time optimization are mentioned in this page:

The Server VM contains an advanced adaptive compiler that supports many of the same types of optimizations performed by optimizing C++ compilers, as well as some optimizations that cannot be done by traditional compilers, such as aggressive inlining across virtual method invocations. This is a competitive and performance advantage over static compilers. Adaptive optimization technology is very flexible in its approach, and typically outperforms even advanced static analysis and compilation techniques.

Transfigure answered 14/2, 2016 at 15:57 Comment(0)
P
11

I have studied outputted Java bytecode in the past (using an app called FrontEnd). It basically doesn't do any optimization, except for inlining constants (static finals) and precalculating fixed expressions (like 2*5 and "ab"+"cd"). This is part of why is is so easy to disassemble (using an app called JAD)

I also discovered some interesting points to optimize your java code with. It helped me improve speeds of inner-loops by 2.5 times.

A method has 5 quick-access variables. When these variables are called, they're faster than all other variables (probably because of stack maintainance). The parameters of a method are also counted to these 5. So if you have code inside for loop which is executed like a million times, allocate those variables at the start of the method, and have no parameters.

Local variables are also faster than fields, so if you use fields inside inner loops, cache these variables by assigning them to a local variable at the start of the method. Cache the reference not the contents. (like: int[] px = this.pixels;)

Perdu answered 12/5, 2011 at 17:27 Comment(2)
Constant expression handling (which includes final variables - not necessary static, if they have a constant value) is actually prescribed by the language specification.Nomi
Can you provide more information about these "5 quick-access variables"? I'm aware of the aload_<n> byte code instructions, but there are only 4 of them, from 0 to 3 (the same for astore_<n>). I'm not even sure if they perform measurable better than the normal aload operation (except that they don't need the extra index byte, so safe some space). I agree with the "local variables vs. fields" argument, though.Moleskin
L
2

To optimize your bytecode, you can use Proguard.

As others have noted, the JIT in a mainstream JVM will optimize the code while compiling it. It will probably outperform Proguard, because it has access to more context. But ths may not be the case in more simple VMs. In the Android world it is common practice to use Proguard optimizations when targeting Dalvik (the VM that came with Android before Lollipop).

Proguard also shrinks and obfuscates the bytecode, which is a must when shipping client side applications (even if you don't use the optimizations).

Loeffler answered 10/4, 2017 at 13:7 Comment(0)
O
0

The compiler don't optimize the bytecode because it is optimized at run time by the JIT optimizer.

If the type of runtime you are targeting don't have a JIT optimizer (even if it had a JIT compiler), or you are AOT compiling, I recommend using an optimizing obfuscator like Proguard or Allatori.

Overscrupulous answered 12/12, 2019 at 11:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.