I have the following code in my app:
public static final boolean DEBUG = true;
public static final boolean REL = !DEBUG;
private static final String DEBUG_OR_RELEASE = (REL) ? "RELEASE_VER" : "DEBUG_VER";
I thought that the Java compiler will eliminate the "DEBUG_VER" string completely from the resulting .apk file (when exported via Proguard) but when I examine the .apk file, I see the "DEBUG_VER" string in there.
Why? What am I missing? What did I do wrong?
Compilation to java bytecode is not making optimization (except a very few exceptions).
Many books state for simplification that the 'compilation' phase is when optimization occurs, but it is wrong. When the optimization really takes place, is while bytecode files are processed by JVM. So for the clearance: optimization may occur at the time of compilation bytecode to machine native code, which is done by JVM tools.
Sometimes there is no optimization at all (JVM works in interpreting mode). Sometimes there is some optimization made by JIT (Just In Time complier). And sometimes adaptative optimizer takes care of the optimization (not only optimizing but also profiling code execution in case of additional operations to be performed).
So finally, there is nothing wrong with your file. It's just how java world works.
"But why?" -- you may ask. The reason to keep this 'useless' information in bytecode is because you would never be sure how many code can you eliminate so the optimization provided by different JVM could still work efficient. The best way is just to not erase any information and let the optimizers to do their job.
According to what you posted, DEBUG is true, so REL is false, so (REL) ? "RELEASE_VER" : "DEBUG_VER" should yield "DEBUG_VER".
This is exactly what you are observing so if you are expecting to see "RELEASE_VER" instead, you should set:
public static final boolean DEBUG = false;
Try that and see what happens.
It's still being run even when the variable is set to true. ?: statements are part of executable and operational code, and since Java doesn't have a precompiler, it will be run every time you compile.
Java does not infer and simplify operations automatically, even if logically the operations are the same each iteration.
Related
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
Is there any differences in code optimization done by same versions of:
Oracle Java compiler
Apache Java compiler
IBM Java compiler
OpenJDK Java compiler.
If there is what code would demonstrate different optimizations? Or are they using same compiler? If there is no known optimization differences then where could I find resources on how to test compilers for different optimizations?
Is there any differences in code optimization done by same versions of: Oracle Java compiler Apache Java compiler IBM Java compiler OpenJDK Java compiler.
While compiler can be very different, the javac does almost not optimisations. The main optimisation is constant inlining and this is specified in the JLS and thus standard (except for any bugs)
If there is what code would demonstrate different optimizations?
You can do this.
final String w = "world";
String a = "hello " + w;
String b = "hello world";
String c = w;
String d = "hello " + c;
System.out.prinlnt(a == b); // these are the same String
System.out.prinlnt(c == b); // these are NOT the same String
In the first case, the constant was inlined and the String concatenated at compile time. In the second case the concatenation was performed at runtime and a new String created.
Or are they using same compiler?
No, but 99% of optimisations are performed at runtime by the JIT so these are the same for a given version of JVM.
If there is no known optimization differences then where could I find resources on how to test compilers for different optimizations?
I would be surprised if there is one as this doesn't sound very useful. The problem is that the JIT optimises pre built templates of byte code and if you attempt to optimise the byte code you can end up confusing the JIT and having slower code. i.e. there is no way to evaluated an optimisation without considering the JVM it will be run on.
No, they do not use the same compiler. I can't comment much about the optimizations and stuffs, but here's an example how the compilers are different in their working.
public class Test {
public static void main(String[] args) {
int x = 1L; // <- this cannot compile
}
}
If you use the standard java compiler, it'll throw an compilation error and the class file won't be created.
But if you use the eclipse compiler for java ECJ, it'll not only throw the same compilation error, but will also create a class file(YES, a class file for an uncompilable code, which makes ECJ, I wouldn't say wrong, but a bit tricky), which looks something like this.
public static void main(String[] paramArrayOfString)
{
throw new Error("Unresolved compilation problem: \n\tType mismatch: cannot convert from long to int.\n");
}
Having said that, this is just between 2 compilers. Other compilers may have their own way of working.
P.S: I took this example from here.
The only compilers that I have spent a great deal of time with are javac (which, as others have pointed out, does very little in terms of eager optimization) and the Eclipse compiler.
While writing a Java decompiler, I have observed a few (often frustrating) differences in how Eclipse compiles code, but not many. Some of them could be considered optimizations. Among them:
The Eclipse compiler appears to perform at least some duplicate code analysis. If two (or more?) blocks of code both branch to separate but equivalent blocks of code, the equivalent target blocks may be flattened into a single block with multiple entry jumps. I have never seen javac perform this type of optimization; the equivalent blocks would always be emitted. All of the examples I can recall happened to occur in switch statements. This optimization reduces the method size (and therefore class file size), which may improve load and verification time. It may even result in improved performance in interpreted mode (particularly if the interpreter in question performs inlining), but I imagine such an improvement would be slight. I doubt it would make a difference once the method has been JIT compiled. It also makes decompilation more difficult (grrr).
Basic blocks are often emitted in a completely different order from javac. This may simply be a side effect of the compiler's internal design, or it may be that the compiler is trying to optimize the code layout to reduce the number of jumps. This is the sort of optimization I would normally leave to the JIT, and that philosophy seems to work fine for javac.
I have code that all over the place(probably 20-30 instances) does this:
<widget>.setVisible((condition == VIEW) || (condition == EDIT));
Logically I do understand that the Java compiler should and probably will optimize this to calculate it up front and simply use the calculated boolean.
My question is is there any way to actually verify that this is the case?
CLARIFICATION
condition is a private class member with no way to modify it past construction.
Disassemble the class file with javap, e.g.
javap -c com.example.ClassName
to see the bytecode source. But why not just guarantee the optimization by extracting into a temp boolean variable?
is there any way to actually verify that this is the case?
You could try to verify this by looking at the bytecode disassembly, then at the machine code produced by HotSpot, etc.
However, I think this strategy is fraught with difficultly. It is also fragile, since the result depends on the compiler/JVM and on the exact nature of condition (is it local? a class member? final? volatile?)
If you care about the optimization for performance reasons [1], my advice would be to perform it manually, by factoring out the common expression.
[1] I assume that you've profiled the code, and know for a fact that this is a bottleneck.
Disassemble the byte code.
That said, these kinds of optimizations may be done at run time, may depend on the declaration of condition, depend on where the conditions are located, and so on. Tracking down optimizations such as this is non-trivial in all but the simplest use cases.
This question has received a total of several paragraphs of answer. Here is the only sentence that actually tells me what I was looking for:
Your examples would make little difference since intermediate computations need to be stored temporarily on the stack so they can be used later on.
In fact, it answers my question perfectly and completely =)
Unlike all the cruft telling me "nooo don't ask that question". >_<
Like if you have a method, and you change it by increasing the number of local variables but make no other changes, does it make the method slower? Here's an example:
void makeWindow() {
Display
.getContext()
.windowBuilder()
.setSize(800, 600)
.setBalloonAnimal(BalloonAnimal.ELDER_GOD.withColor(PUCE))
.build();
}
or
void makeWindow() {
DisplayContext dc = Display.getContext();
WindowBuilder wb = db.windowBuilder();
BalloonAnimal god = BalloonAnimal.ELDER_GOD;
BalloonAnimal puceGod = god.withColor(PUCE);
wb.setSize(800, 600).setBalloonAnimal(puceGod).build();
}
Another example:
int beAnExample(int quiche) {
return areGlobalsEvil?
quiche * TAU/5:
highway(quiche, Globals.frenchFrenchRevolution);
}
or
int beAnExample(int quiche) {
if (areGlobalsEvil) {
int to5 = TAU/5;
int result = quiche * to5;
return result;
} else {
Game french = Globals.frenchFrenchRevolution;
int result = highway(quiche, french);
return result;
}
}
Really, what I'm asking is: Is the number of this sort of local variable even relevant by the time the method's compiled to bytecode? If so, what about once Hotspot gets to work on it?
This question is relevant to the code generator I'm working on.
The easy answer is no. Local variables consume runtime stack space. Allocating space for them only marginally increases the number of instructions. Your examples would make little difference since intermediate computations need to be stored temporarily on the stack so they can be used later on. Focus more on the readability of your programs rather than needless micro-optimizations.
If you're interested in looking at the actual bytecode of a class, investigate the javap program.
Don't worry about it. The compiler can do all sorts of crazy, make-your-head-asplode optimizations. Start with code that's correct and maintainable. Programmer time is worth far more than processor tiem.
Test it by running each method 1,000,000 times and divide the total time to calculate the cost per execution. In all likelihood, it won't be noticable.
Actually, Java compilers may even be smart enough to just compile it out.
Write your code for readability to reduce long term cost of maintenance. Then tune it in the 5% of places where you really need to.
The chances are that it will make little (if any) difference, and the "little" will be insignificant.
Focus on making your generator correct and maintainable, and let the Java compiler (particularly the JIT compiler) do the micro-optimization of the generated code.
Note that #Edawg's advice on looking at the bytecode is not necessarily helpful. The JIT compiler aggressively optimizes the native code that it generates from the bytecodes. It can be difficult to predict which of two bytecode sequences is going to be faster. Certainly, counting bytecodes, method calls and so on can be misleading.
The only way to be sure that you are generating "optimal" Java source code would be to compile it and benchmark it on your target platform. Even then, there's a good chance that your efforts at source-code level optimization will be negated ... by JIT compiler changes.
This is not a hotspot issue. There may need to be additional byte codes to load and store the local variables, but let the compiler worry about optimizing that.
You should concentrate on issues like null pointer checking in your generated code and how to report errors in a meaningful way that is tied to the source input that you are code generating from.
Suppose I have code like :
log.info("Now the amount" + amount + " seems a bit high")
and I would replace the log method with a dummy implementation like :
class Logger {
...
public void info() {}
}
Will the optimizer inline it and will the dead code removal remove the parameter construction if no side effects are detected?
I would guess that the compiler (javac) will not, but that the just in time compiler very likely will.
For it to work however, it has to be able to deduct that whatever instructions you use to generate the parameter have no side effects. It should be able to do that for the special case of strings, but it might not for other methods.
To be sure, make a small benchmark that compares this with the code that Jesper suggested and see, whichever is faster or if they are equally fast.
Also see here: http://www.ibm.com/developerworks/java/library/j-jtp12214/index.html#3.0
The important answer is: it might do.
I don't think you should be relying on any particular behaviour of the optimiser. If your code runs acceptably fast without optimisation, then you may or may not get a performance boost, but the good news is your solution will be fine regardless. If performance is dreadful when non-optimised, it's not a good solution.
The problem with relying on the optimiser is that you're setting yourself up to conform to an invisible contract that you have no idea of. Hotspot will perform differently in other JDK/JRE versions, so there's no guarantee that just because it runs fine on your exact JVM, it'll run fine elsewhere. But beyond that, the exact optimisations that take place may depend on environmental issues such as the amount of free heap, the amount of cores on the machine, etc.
And even if you manage to confirm it works fine in your situation right now, you've just made your codebase incredibly unstable. I know for a fact that one of the optimisations/inlinings that Hotspot does depends on the number of subclasses loaded and used for a non-final class. If you start using another library/module which loads a second implementation of log - BANG, the optimisation gets unwound and performance is terrible again. And good luck working out how adding a third party library to your classpath toasts your app's internal performance...
Anyway, I don't think you're asking the real question. In the case you've described, the better solution is not to change the info method's implementation, but change the calls to no-ops (i.e. comment them out). If you want to do this across a whole slew of classes at once, at compile time, you can use a type of IFDEF like so:
public class Log
{
public static final boolean USE_INFO = true;
public void info()
{
...
}
...
}
and then in your class:
if (Log.USE_INFO)
{
log.info("Now the amount" + amount + " seems a bit high");
}
Now the compiler (javac, not Hotspot) can see that the boolean condition is constant and will elide it when compiling. If you set the boolean flag to false and recompile, then all of the info statements will be stripped from the file completely as javac can tell that they will never be called.
If you want to be able to enable/disable info logging at runtime, then instead of a constant boolean flag you'll need to use a method and here's there's nothing for it but to call the method every time. Depending on the implementation of the method, Hotspot may be able to optimise the check but as I mentioned above, don't count on it.