4

So I was running some benchmarks on different data structures and noticed, that when I declared my variables final I got 10-20% performance increases.

That really surprised me. I thought the final keyword is purely used for restricting change in variables and optimization would figure out if some variable is of constant value or not.

Here is the example:

import javafx.scene.input.KeyCode;
import java.util.*;

public class Main {
    static /*final*/ int LOOPS = Integer.MAX_VALUE / 100;

    static /*final*/ KeyCode[] keyCodes = KeyCode.values();

    public static void main(String[] args) {
        long startTime;
        long endTime;

        testEnumSet(); //warmup
        startTime = System.nanoTime();
        testEnumSet();
        endTime = System.nanoTime();
        System.out.println("  EnumSet: " + (endTime - startTime) + "ns");
    }

    static /*final*/ EnumSet<KeyCode> enumSet = EnumSet.noneOf(KeyCode.class);
    static void testEnumSet() {
        for (int i = 0; i < LOOPS; i++) {
            /*final*/ KeyCode add = getRandomKeyCode();
            if(!enumSet.contains(add)) enumSet.add(add);

            /*final*/ KeyCode remove = getRandomKeyCode();
            if(enumSet.contains(remove)) enumSet.remove(remove);
        }
    }

    /*final*/ static Random random = new Random();
    static KeyCode getRandomKeyCode() {
        return keyCodes[random.nextInt(keyCodes.length)];
    }
}

With final: .... EnumSet: 652 266 207ns
Without final: EnumSet: 802 121 596ns

This is consistently reproducible!

Why is there such an enourmous difference between the code that uses final and the code that doesn't? Why doesn't it get optimized? And why is final faster anyway, what is the difference in the generated bytecode?

Jhonny007
  • 1,698
  • 1
  • 13
  • 33
  • [Duplicate](http://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance?rq=1) –  Sep 15 '16 at 23:16
  • 1
    @JarrodRoberson the top rated answer sais there is no performance gain, but my benchmarks show ~20% perfomance gain so I don't think this is a duplicate. – Jhonny007 Sep 15 '16 at 23:18
  • top rated does not mean always mean *correct* sometimes it just means most people are wrong. Just like your findings will not always be reproducable and `final` will not always grant any measurable gains. –  Sep 15 '16 at 23:21
  • 2
    You're using *static* final fields in your code. Convert it to a final instance fields (just create an instance in `main`) and the effect will probably disappear since static fields participate in constant folding. Instance fields otoh are not constants (changing from instance to instance) and thus only benefit from redundant load elimination, which only relies on non-volatileness and not finalness. So if you want to benchmark immutable objects and not just global constants your benchmark is not useful. – the8472 Sep 16 '16 at 11:07
  • Yeah you are right, although that was not the goal of my benchmark it was just something I discovered on the side and got me wondering. – Jhonny007 Sep 16 '16 at 12:00
  • Well, most notable, `LOOPS` will be a *compile-time constant* when being declared `final` which implies that it’s value gets inlined. In other words, for this variable, adding the `final` modifier will actually change the *code* using it. For the local variables, on the other hand, it has the least impact (none at all), as the information whether these variables were declared `final` is not present in the class. So the effect of `final` can vary greatly. – Holger Sep 16 '16 at 16:44

1 Answers1

4

If something can never change, you can do all kinds of optimizations like in-lining of the actual value instead of looking it up over and over again. This is just one thing you can do that is easy to explain and gives the greatest benefit.

There are many other more esoteric things that happen that have much less impact.

If you look at the bytecode you will see this, especially after the JIT kicks in.

Making the entire class final can have similar benefits.

That said, final references will not always provide measurable gains, it depends on the usage of the reference. In this case EnumSet does a lot of special sauce stuff under the hood if you look at the source. Immutable references probably get inlined as part of that.

Also note that the behavior you are seeing might go away in future release of the JVM, or not be there in other JVM implementations. Anything is subject to change out from under you so don't rely on any one specific implementation.

Here is some more information in greater detail about all the idiomatic uses of final.

  • Yeah, but I thought the JVM was smart enough to detect that a variable is basicly final if it is never modified (especially in such a simple piece of code) and is able to optimize it. – Jhonny007 Sep 15 '16 at 23:16
  • no it can not predict the future, anything that is not explicitly limited from changing could possibly change anytime in the future, just because it has not changed in the last 100K executions does not mean it won't for the 100,001st. –  Sep 15 '16 at 23:17
  • But isn't that something that could be figured out at compile time if a field gets changed? I mean if you try to change a final field the compiler notices and stops you. – Jhonny007 Sep 15 '16 at 23:24
  • how do you know a reference value has changed if you don't check it at every usage at runtime? It is not magic, that indirection of the reference to get to the value is expensive over a long time. There is the `volatile` key word to disable any soft caching of values across threads. Some basic research into this subject, which is too broad for SO, will tell you what you want to know. –  Sep 15 '16 at 23:26
  • As I said if you couldn't check if a reference value changes at compile time the final keyword for fields would not work and the compiler couldn't stop you from compiling. Or am I missing something? – Jhonny007 Sep 15 '16 at 23:30
  • To illustrate it more: Let's assume the compiler treats every variable as `final` from the beginning and makes every variable that violates `final` conditions non-final than you would easily achieve the optimization. – Jhonny007 Sep 15 '16 at 23:38
  • you can't predicate the future because there is code that might be changing the field that is not available at compile time, code that calls code that is already compiled can't be predicted. Like I said, this is way too-broad, I should have not answer it in the first place in all honesty. –  Sep 16 '16 at 06:31
  • Oh I understand now. So if you build a library and everything would be optimized to final the user of the library couldn't use it. – Jhonny007 Sep 16 '16 at 07:40
  • 1
    HotSpot is good at optimizing non-`final` variables, but this example invokes a `synchronized` method on a globally visible `Random` instance, which implies that the code *must* re-check for potential in-between modifications made by other threads. For the `KeyCode[] keyCodes` variable, it implies performing boundary checks again, which is the reason why for this variable, being `final` or not has the biggest impact. Then, there is `int LOOPS = Integer.MAX_VALUE / 100` which is a *compile-time constant* when being declared `final`. For all other variables, the impact is rather small. – Holger Sep 16 '16 at 16:57
  • @JarrodRoberson true if the raw fields are not private, but your point seems moot for local variables or private fields. As Jhonny007 said, both the code inspector and the compiler can detect if a final field is written to and if the field can be made less accessible, so it seems reasonable that the compiler could have the option to auto-optimize marking local variables and private fields as final, and that that behavior would be desirable by default [especially for release builds]. I ASSuMEd this is how modern Java compilers worked. :/ – swooby Mar 15 '17 at 19:48