Stunning Java8 Stream Performance

I can’t understand why iterating several times over the same array using Stream api leads to such performance!

see code below.

public class WhyIsDifferent { public static void main(String[] args) { int[] values = getArray(); Iterate(values, 598, 600); // 70 ms Iterate(values, 200, 202); // 0 ms Iterate(values, 700, 702); // 0 ms Iterate(values, 300, 310); // 1 ms } public static void Iterate(int[] values, int from, int to) { long start = System.currentTimeMillis(); IntStream.of(values).filter(i -> i < to && i > from) .forEach(i -> System.out.println(i) // do a something ); System.out.println("Time:" + (System.currentTimeMillis() - start)); } public static int[] getArray() { int[] values = new int[1000]; for (int i = 0; i < 1000; i++) { values[i] = i; } return values; } } 

Surely the JVM optimizes the code, but I don’t know how this happens ??? This is amazing! Do you know why this is happening?

-

I am testing Ubuntu 14.04 // Oracle jdk / intel cpu.

+5
source share
1 answer

This is not a JIT compiler. Most of these 70 milliseconds were spent on initializing the entire lambda subsystem (the entry point to this logic is probably the LambdaMetaFactory class), and a good bit is also spent on calling the lambda bootstrap (the binding stage, as specified by user fge). Check out this method, just like yours, but with all the individual dimensions (and I'm using nanoTime ):

 public static void Iterate(int[] values, int from, int to) { long start = System.nanoTime(); final IntPredicate predicate = i -> i < to && i > from; System.out.println("Predicate lambda creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); start = System.nanoTime(); final IntConsumer action = System.out::println; System.out.println("Action lambda creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); start = System.nanoTime(); final IntStream stream = IntStream.of(values).filter(predicate); System.out.println("Stream creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); start = System.nanoTime(); stream.forEach(action); System.out.println("Stream consumption time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); } 

This is what was printed on my machine:

 Predicate lambda creation time:53 Action lambda creation time:2 Stream creation time:2 599 Stream consumption time:1 Predicate lambda creation time:0 Action lambda creation time:0 Stream creation time:0 201 ... all timings zero from here on... 

You can see that all the overhead of the first call is in the lambda creation part (which includes initialization and binding at the first start), and creating a thread also takes some time. Actual flow consumption takes zero time in all cases.

This effect, of course, is something to keep in mind with the current version of HotSpot: lambda bootstrap is an expensive thing.

Final note: if you change the order of the lambda creation statements, you will see that most of the time is left with the first lambda to be created. This shows us that this is actually just the first general lambda creation that carries most of the initialization cost.

+9
source

Source: https://habr.com/ru/post/1207608/


All Articles