5

Given the following code snippet in which an ad hoc instance of a Set is created, will todays Java compiler see that they do not need to create the instance for each loop pass and optimize set as a kind of final constant such that the instance is the same for the entire loop? The question applies similarly to while and do while loops.

for (/* a loop that iterates quite often */)
{
   var set = Set.of("foo", "blah", "baz");
   // Do something with the set.
}

I would also be interested whether such an optimization is done already at compile-time (meaning that the byte code is already optimized) or whether there exist runtime optimizations (made by the just-in-time compiler) that essentially achieve the same.

If a compiler does not see this, the only option seems to instantiate the set outside the loop in order to be optimal:

var set = Set.of("foo", "blah", "baz");
for (/* a loop that iterates quite often */)
{
   // Do something with the set.
}

Side note, for those finding the first snippet rather bad practice and that would write it like the second snippet anyway: This snippet is actually a simplified variant of my real use case, made for simplicity reasons of the question. The real, slightly more complex use case that brought me to this question is the following, where a string needs to be checked whether it is one of a very few strings:

for (/* a loop that iterates quite often */)
{

   // ...

   var testString = // some String to check ...
   if (Set.of("foo", "blah", "baz").contains(testString))
   {
      // Do something.
   }

   // ...

}

Assuming that the if condition is surrounded by additional code (i.e., the loop body is rather large), I think one wants to declare the set inline rather than further away outside the loop.

twwwt
  • 438
  • 1
  • 4
  • 16
  • 4
    The _compiler_? No. The JIT? Perhaps. The java compiler is pretty basic: it will mostly convert the Java code as written. – Andy Turner Dec 10 '21 at 09:17
  • Yup. JIT is voodoo. That's where all the optimising smarts happen. – Dawood ibn Kareem Dec 10 '21 at 09:27
  • 1
    That's known as "hoisting", and it is one tool that JIT has. It's at least well within the realm of possibilities. Similar question with comments [here](https://stackoverflow.com/q/52423441/2541560). – Kayaman Dec 10 '21 at 09:45
  • OTOH, it's also well within the realm of possibilities for a human to do. It would tweak my spidey sense to have such a thing inside a loop. – Andy Turner Dec 10 '21 at 09:46
  • In the question I linked, they eventually get around to properly benchmarking it and it doesn't manage to hoist the stream out of the loop. So the same might apply here. The only way to know for sure is to measure performance. – Kayaman Dec 10 '21 at 09:56
  • 1
    You could compile the two variants and decompile them using `javap -c`. As one with a compiler construction background, I fear the worst. – Joop Eggen Dec 10 '21 at 10:35
  • You can use `System.identitiHashCode` to find out but what is the case for your device may not be for other devices. – dan1st Dec 10 '21 at 10:45
  • 1
    Beware: if you use `identityHashCode`, that may actually inhibit the JIT compiler from optimizing. If an optimization is going to alter the output of a program (i.e. the printed hash codes) then it is arguably not a valid / safe optimization to make. The best option is to get the JIT compiler to print the native code it generates. – Stephen C Dec 10 '21 at 13:52

1 Answers1

4

The answer will inevitably depend on the Java tools that you use.

The javac bytecode compiler is (deliberately) naive, and I would not expect it to do any optimization of this. The JVM's JIT compiler may optimize this, but that will depend on:

  • your Java version,
  • the JIT compiler tier used
  • what /* do something with the set */ actually entails.

If this really matters to you, I would advise a couple of approaches.

  1. Rewrite the code yourself to hoist the Set declaration out of the loop. IMO, there is nothing fundamentally wrong with this approach, though it is potentially a premature optimization. (I'd probably do this myself in the examples you gave. It just "feels right" to me.)

  2. Use the JVM options for dumping out the native code produced by the JIT compiler and see what it actually does. But beware that the results are liable to vary: see above.

But even if you know with a good degree of certainty how the compilers will do, it is not clear that you should be worrying about this in general.


Note looking at the bytecodes with javap -c will tell you if the java compiler is doing any optimization. (Or more likely, confirm that it isn't.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216