2

I have lists that I need to iterate through. I was using the new foreach from Java 8. I found it to be much slower than older less elegant methods. I've read this stack discussion which focuses more on best practices rather than real world implementation. I understand that with the lambda there is additional overhead that stems from method passing, but I still don't understand the discrepancy between the first and third case and that has me thinking I could be using it wrong. Are there not yet implemented optimizations for the lambda or is there a better way I could be using them to get more performance? Below is a demonstrative sample and not my actual code.

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class Main {
        public static void main(String[] args) {

            List<Memes> memesList =
                    Arrays.asList(
                            new OldMemes(),
                            new TrendingMemes()
                    );

            //----------------------------Benchmark
            long start, end;
            start = System.nanoTime();
            System.out.format("for each started @%d\n", start);
            //Old way.
            for(Memes memes : memesList){
                memes.showMeme();
            }
            //-----------------------------Benchmark
            end = System.nanoTime();
            System.out.println((end - start));
            start = System.nanoTime();
            System.out.format("for started @%d\n", start);
            //Other way
            for(Iterator<Memes> i = memesList.iterator(); i.hasNext(); ) {
                i.next().showMeme();
            }
            //-----------------------------Benchmark
            end = System.nanoTime();
            System.out.println((end - start));
            start = System.nanoTime();
            System.out.format("for each lambda started @%d\n", start);

            //New way.
            memesList.forEach((memes) -> memes.showMeme());

            //-----------------------------Benchmark
            end = System.nanoTime();
            System.out.println((end - start));
            System.out.format("end @%d\n", end);
        }
    interface Memes {
        public void showMeme();
    }

    static class OldMemes implements Memes {

        @Override
        public void showMeme() {
            System.out.println("Id appetere senserit his, nonumes consulatu vel id.");
        }

    }

    static class TrendingMemes implements Memes {

        @Override
        public void showMeme() {
            System.out.println("Id appetere senserit his, nonumes consulatu vel id.");
        }
    }
}
Community
  • 1
  • 1
ialexander
  • 898
  • 10
  • 28
  • 1
    Your benchmark is flawed + most of the time is spent in the `showMeme` method which prints (that is a very slow operation vs. iterating)... – assylias Jan 03 '14 at 12:08
  • @assylias I understand the benchmark is flawed per the answer below. Are you saying that the Java 8 forEach will be slower than other methods when there is a slow operation taking place inside, or are you saying it is just impossible to tell which is slower using the flawed benchmark? – ialexander Jan 03 '14 at 12:21
  • 1
    I am saying that your benchmark is not measuring what you think it is measuring so I don't think you can draw any conclusions from it. – assylias Jan 03 '14 at 12:22
  • 1
    There is a difference between the very first execution of a piece of code when the lambda needs to be instantiated and all subsequent executions of that code when the lambda is just reused. It’s like using a class the first time; it has to be loaded and verified and measuring that time does not say anything about the loop in which you use the instance. That’s why you should execute a code multiple times when doing benchmarks and throw away the results of the first executions. Using loops with very few iterations like in your example make it just worse. – Holger Jan 03 '14 at 12:55
  • 2
    It is slower for sure because the streams API with lambdas set up a lot of scaffolding code. In other words, lambdas are a lot slower. Although your test is flawed, the real life sad conclusion is that ` forEach` from the Streams api is very slow compared to a normal use of iterators or for-loops with index. – The Coordinator Jan 09 '14 at 21:56

1 Answers1

9

Your benchmark is terribly wrong. You only apply one loop (of only two elements) for both techniques. You should repeat this at least for a trillion times. Also increase the size of your array, use something of at least 50 elements. Next, do never print to stdout while benchmarking. That takes too much time and is a synchronized operation and will give wrong results.

Read this topic to get better results: How do I write a correct micro-benchmark in Java?

Community
  • 1
  • 1
Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287