For example I have expression like this
if (DEBUG) log.debug("hello")
Is there a way in scala or java to initiate DEBUG
variable in such way, so that if it is false
whole expression is removed in runtime?
My guess that branching will be removed by JIT in runtime, but is it? Is there a way to "give a hint" to JIT to do it?
If a value is a constant, mark it static final
. This also protects you from accidential modifications. If you need the variable to be per-instance (e.g. set in the constructor), just make it final
. The JIT will also be able to make use of that information.
But the JIT is not the only compiler capable of using that information.
Let's say you have the following Java code:
class A{
static final boolean DEBUG = false;
void a(){
if(DEBUG){
System.out.println("Hello World");
}
}
}
and you look at the bytecode generated by javac
(using javap -c A.class
), it will look something like that:
Compiled from "A.java"
class A {
static final boolean DEBUG;
A();
Code:
0: aload_0
1: invokespecial #3 // Method java/lang/Object."<init>":()V
4: return
void a();
Code:
0: return
}
As you see, javac
(I don't know about the Scala compiler though) is already capable of eliminating some branches originating from static final
fields if they are initialized with a literal. But this also means you need to recompile all classes using that field if you change your DEBUG
variable.
If you want to avoid that and still be sure the JIT optimizes it away at runtime, you can do something like:
static final boolean DEBUG = initDebug();
private static boolean initDebug(){
return DEBUG;
}
Now, even if javac
can't inline it, there is a lot more the JIT can do. It can see that a variable is final
, assume it is never changed and optimize for that.
But even if the result comes from a method that is called over and over and just happens to always return the same value, even if the JIT cannot inline the value, it can still detect that some branch is never executed. In that case, it typically replaces this branch with a deoptimization trap (if it would need to be called, the JIT deoptimizes the code and starts again). This would only happen for code that gets executed sufficiently often (and for other code, you don't need to care as it's normally irrelevant for performance).
In many cases, the things you think are important for performance are irrelevant. If you ever think you should optimize something, please first check whether it is actually a bottleneck. You can use flamegraphs or similar tools for this.
Once you get to the point of optimizing a piece of code, first use JMH to actually measure the performance without the optimization. After doing your optimizations, measure it again (with JMH) and check whether it improved. If you don't see a significant improvement (or it even gets worse), the optimization is probably not worth it. You don't know which code will perform well and your optimizations may make performance worse because they could make some JIT optimizations harder or have similar issues.
When writing Java applications, don't optimize for performance unless you know it is a problem. The JIT makes sure that what is relevant for performance is optimized well. Don't try to interfere with it unless you know that part of the application causes problems for your application and check whether your "optimizations" actually improve anything.