2

I have an ArrayList around 3483 object that ready to merge in database. When I use lambda expressions - it takes 85122 ms. But for(Obj o : list) takes only 25 ms. Why Java8 takes 3404 times more time ?

List<CIBSubjectData> list1 = .....

list1.forEach(data ->
     merge(data)
);

for (CIBSubjectData data : list1) {
    merge(data);
}
Mr. Mak
  • 837
  • 1
  • 11
  • 25
  • 1
    Tried the parallel stream: list1.parallelStream().forEach(data -> merge(data)) ? – ernest_k May 25 '16 at 11:05
  • 1
    you might find your answer int [this question](http://stackoverflow.com/questions/16635398/java-8-iterable-foreach-vs-foreach-loop). – SomeJavaGuy May 25 '16 at 11:05
  • 2
    I think we'll need more context and/or code here. `forEach`+lambda does take longer, but nowhere near *that* much longer, in the general case. I just did a trivial check (which is [not reliable](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java)) on a `LinkedList` with 400k entries, `forEach` took 40ms; enhanced `for` look 5ms. – T.J. Crowder May 25 '16 at 11:14
  • What do you do inside `merge(data)`? – SubOptimal May 25 '16 at 11:57

1 Answers1

3

I belive you are not using a proper microbenchmark setting. You are comparing the warmup of the bytecode instrumentation framework (ASM which is used to generated the lambda bytecode at runtime) + lambda execution time with the execution time of the loop.

Check this answer for performance-difference-between-java-8-lambdas-and-anonymous-inner-classes and the linked document. The linked document has a deep insight about the processing under the hood.

edit To provide a small snippet to demonstrate the above.

public class Warmup {
    static int dummy;

    static void merge(String s) {
        dummy += s.length();
        dummy++;
        dummy -= s.length();
    }

    public static void main(String[] args) throws IOException {
        List<String> list1 = new ArrayList<>();
        Random rand = new Random(1);

        for (int i = 0; i < 100_000; i++) {
            list1.add(Long.toString(rand.nextLong()));
        }

        // this will boostrap the bytecode instrumentation
        // Stream.of("foo".toCharArray()).forEach(System.out::println);
        long start = System.nanoTime();
        list1.forEach(data -> merge(data));

        long end = System.nanoTime();
        System.out.printf("duration: %d%n", end - start);
        System.out.println(dummy);
    }
}

If you run the code as it is posted the printed duration on my machine is

duration: 71694425

If you uncomment the line Stream.of(... (which is only there to use the the bytecode instrumentation framework the first time) the printed duration is

duration: 7516086

Which is only around 10% of the initial run.

note Only to be explicit. Don't use benchmarks like the above. Have a look at jmh for such a requirement.

Community
  • 1
  • 1
SubOptimal
  • 22,518
  • 3
  • 53
  • 69
  • 1
    OP reports "85122 ms"; that's one and a half minutes. I would not call this "microbenchmarking", and don't think that it's all due to warm-up, either... – tobias_k May 25 '16 at 11:34
  • 1
    @tobias_k I agree. But `for(Obj o : list) takes only 25 ms` it is. So he for sure want's to make a microbenchmark. Otherwise the loop would also take much longer. For the point that lambda take much longer. We need more input from the OP. – SubOptimal May 25 '16 at 11:57