Improving performance using Iterable.forEach()

Since Java 8 there is a new Iterable.forEach() method, which allows you to perform an operation upon each item of the iterated bunch of data. One might wonder why this method was added, because you could do the same using Java’s for statement since version 5 of the JRE.

There are two answers. The first is coding style. But maybe you are more interested in the second: Performance.

While working on the Babbler XMPP library, we discussed whether there will be a measurable speed difference between the for statement and the forEach method, so a benchmark was used to give the final answer. And it was an impressive answer: Once the virtual machine was warmed up, the forEach statement outperformed the for statement by far!

If you are courious why that happens, just follow our steps…:

Benchmark using for statement

public static final void main(final String[] arguments) {
  for (int j = 0; j < 10; j++) {
    final List<String> list = new ArrayList<>();
    for (int i = 0; i < 5000000; i++)
       list.add(UUID.randomUUID().toString());
    final long start = System.currentTimeMillis();
    for (final String s : list)
      s.length();
    System.out.println("Foreach loop: " + (System.currentTimeMillis() - start));
  }
}

Benchmark using forEach method

public static final void main(final String[] arguments) {
  for (int j = 1; j <= 10; j++) {
    final List<String> list = new ArrayList<>();
    for (int i = 0; i < 5000000; i++)
      list.add(UUID.randomUUID().toString());
    final long start = System.currentTimeMillis();
    list.forEach(String::length);
    System.out.println("Foreach loop: " + (System.currentTimeMillis() - start));
  }
} 

Compiler options

javac -g:none -O Benchmark.java

Decompiler options

javap -c Benchmark.java

Look at the decompiler’s output for both versions of the benchmark, and you’ll understand what’s going on…!

One part of the magic  comes from the fact that the JVM applies several optimizations to repeatedly executed code, like inlining, native compilation, etc. The use of the for statement in this code, and particularly its custom body, exist solely for the sake of this Benchmark, while the forEach method are common code, likely to be used by other methods of the same application (does not apply to this Benchmark, obviously). That increases likelihcood of such optimizations.

The other part of the magic (which does apply to this Benchmark) comes from the fact that even when there is language support for iterating Iterables, there is no support built into the JVM. What the JVM actually has to do when you do a for statement, is to invoke several methods of the Iterable interface again and again. The forEach method on the other hand may be (and actually is) implemented by each Iterable in a different way, allowing to access private members natively (without going through the published Iterable methods). And that is exactly what actually happens in ArrayList.forEach!

Conclusion

Sometimes we can learn how to write superior Java code if we take a look into the compiled result using javap. Thanks to Rafael Winterhalter who delivered this message in his inspiring JavaLand talk! In the particular case, we learned that forEach results in less bytecode calling one very fast method, which explains the measured performance benefit. Due to its nature of being a method of a central part of the JRE (the collections framework), it is highly optimized and will always run considerably faster than any custom for statement.

Advertisements

About Markus Karg

Java Guru with +30 years of experience in professional software development. I travelled the whole world of IT, starting from Sinclair's great ZX Spectrum 48K, Commodore's 4040, over S/370, PCs since legendary XT, CP/M, VM/ESA, DOS, Windows (remember 3.1?), OS/2 WARP, Linux to Android and iOS... and still coding is my passion, and Java is my favourite drug!
This entry was posted in Allgemein, Java, Programming and tagged , . Bookmark the permalink.