The short answer is that HotSpot is not targeted to javac code but some static transformation can adversely affect the performance of your application. I'll give some more details below.
Damon also mentioned that there had been some fuss about a VM crash caused by some code generated by jikes. I know that all the HotSpot engineers treat VM crashes with utmost urgency and I checked with Ken Russell. Ken remembers one particular crash where jikes used the invokeinterface bytecode to call methods declared in java.lang.Object. This crash was treated with high priority and has since been fixed.
HotSpot is just a piece of software and may contain bugs but that does not mean javac is getting a better deal. However, there are certain factors which can play a role in how good a job HotSpot can do on your application.
Some of the industry benchmarks are based on the combination of compiler and JVM so if HotSpot seems biased towards javac, that may be the reason but not intentional.
On the other hand, many static optimizers will automatically inline method calls. The fundamental problem with inlining method calls is that if the inlined method was never called by method into which it was inlined it will just make the resulting method bigger (and perhaps prevent it from being inlined in other methods). So although a static optimizer think it is optimizing it has no data about how the program behaves at runtime and may make mistakes.
This is a fundamental problem that will make your programs run slower on HotSpot. The name HotSpot comes from the VM's ability to recognize hotspots in the program based on statistics collected while the program is running. By using this data, HotSpot can generally make better decisions than a static optimizer.
Besides hurting HotSpots ability to inline based on live data, big
methods resulting from premature inlining can have other negative
effects on performance. HotSpot will compile hot
methods
to native machine code. This code is sensitive to CPU level
optimizations such as code caches and branch prediction. I don't know
if this is a real life problem.
I checked with Ken and Ross Knippel to see if there could be other problems and they mentioned that obfuscators sometimes makes some very radical transformations of the control flow of your application. This can cause problems for HotSpots ability to detect loops and optimize them. A similar problem could occur if the compiler generated code that looked very different from what javac and similar compilers generate. However, in Mustang, HotSpot will apply a technique called loop rotation to properly place a loop exit that is testing an induction variable. Ross provided this example:
int i = 0;
while (true) {
stmt1;
i++;
if (i >= n) break;
stmt2;
}
is transformed into
int i = 0;
stmt1;
i++;
if (i < n) {
do {
stmt2;
stmt1;
i++;
} while (i < n)
}
Posted by Andy Tripp on August 04, 2006 at 10:07 AM PDT #
Posted by Peter von der Ahé on August 04, 2006 at 10:22 AM PDT #
I'm honoured by the detailed reply! Thanks!
In the case of ProGuard I don't think it inlines anything in 3.x (but gets rid of some get/set calls, making the relevant fields more public if need be if you ask it to).
One of the things that the ProGuard-style optimisation does that I had wondered about is adding final/static/private attributes where it can given that you tell it advance the entire set of classes in your app. Ie, this is stuff that HotSpot normally deduces at runtime when it has warmed up, but it has been suggested that PG's ahead-of-time analysis *may* upset the heuristics that HotSpot uses.
On this particular point, especially making more things final, is there likely to be any problem? In general my personal coding rule of thumb is "make anything final that you can", and I'd like that not to actually hurt run-time performance!
Rgds
Damon
Posted by Damon Hart-Davis on August 05, 2006 at 10:11 AM PDT #
Posted by 69.238.229.218 on August 05, 2006 at 02:50 PM PDT #
Posted by 69.141.244.128 on August 07, 2006 at 08:01 AM PDT #
Well this statement is wrong - I was told by an hotspot engineer that since sun-java-1.4 the client-compiler also does more advanced stuff but still very careful. I simply read too old whitepapers and java performance reports ;)
However it seems you misunderstood it completly - the client-jvm does a lot static optimizations because these are quite often cheap, however when it comes down to profiling based optimizations client does only a very little bit.
Mfg Clemens Eisserer
Posted by Clemens Eisserer on August 11, 2006 at 12:28 PM PDT #
Posted by Andy Tripp on August 11, 2006 at 01:07 PM PDT #
One interesting thing I am looking foreward is tired-compilation. A prototype already exists in mustang (you can compile it yourself on solaris) ... this feature and a JIT cache and I will be happy forever ;-)
Posted by Clemens Eisserer on August 11, 2006 at 01:51 PM PDT #
Anyway, I've done some tests with ProGuard to get a better feel for the effects of static shrinking, optimization, and obfuscation. I've tested on a P4 2.6 GHz, in Fedore Core 4, with Sun JDKs 1.4.2 and 1.5.0, with -client, -server, and -Xint. I've measured the total execution time (minimum of two runs) before and after having processed the code with ProGuard 3.7 (beta) and 4.0 (alpha) (with 1 and with 6 optimization passes). Version 3.7 removes unused classes, fields, methods, parameters, and local variables, makes classes and methods final and static when possible, performs partial evaluation to eliminate constant expressions, inlines getters and setters, and performs small peep-hole optimizations. Version 4.0 additionally performs constant propagation between methods, and it inlines methods in general. ProGuard is geared toward producing leaner code, so it does not perform string obfuscation or control flow obfuscation.
To cut a long story short, these tests showed only minor performance improvements of up to 2% after static processing. The server JVM seems to have a longer start-up time due the larger methods after inlining, but that loss is then offset by faster execution. The results look like a testament to the efficiency of the Sun JVMs. Processing code may be more interesting for reducing its memory footprint than for improving its performance.
In this test, I processed ProGuard itself, which I consider pretty lean code to start with. Your mileage with other code may vary. A user reported a performance decrease of 10% to 25% for his application in JDK 1.4.2, which seems surprising given my experiences. I still have to investigate this. I also measured total execution time, which is not always appropriate. Finally, I haven't tested individual optimizations, nor other VMs. More tests should provide a better picture, but this is a start.
Eric Lafortune -- author of ProGuard
Posted by Eric Lafortune on August 14, 2006 at 08:00 AM PDT #
Even more detail and facts! Thanks!
Well, I do optimise/shrink mainly in the hope of smaller application size (eg for Java Web Start apps, for which I also obfuscate to squeeze the download size even further) and smaller footprint and faster class loading, and 2% improvement without having to tweak any code seems like a good thing to me, so I'll go on doing it.
But I will think carefully about how much inlining to request from ProGuard 4.x I guess.
Thanks again,
Damon
Posted by Damon Hart-Davis on August 14, 2006 at 10:35 AM PDT #