7 ms·
Interesting approach. It would be nice to see some benchmarks of IntStream vs. traditional loop. I would expect that IntStreams are slower if it does not use t
by c-rack 11y ago
Interesting approach. It would be nice to see some benchmarks of IntStream vs. traditional loop.
I would expect that IntStreams are slower if it does not use the parallel execution, see:
https://stackoverflow.com/questions/22658322/java-8-performance-of-streams-vs-collections https://stackoverflow.com/questions/22658322/java-8-performa...
And even parallel execution might be tricky:
http://zeroturnaround.com/rebellabs/java-parallel-streams-are-bad-for-your-health/ http://zeroturnaround.com/rebellabs/java-parallel-streams-ar...
- Karunamon 11y agoA coworker and I tested this out when the announcement was originally made, at least in the use case of iterating a massive array. (Insert 100M trues and one false at the end, and find me the false) Code is here: https://gist.github.com/Karunamon/abc6483ac1d08f6cc137 https://gist.github.com/Karunamon/abc6483ac1d08f6cc137. The result was that streams were roughly 4x slower. Still, I really like some of the new constructs that Java is getting - they make the language a bit more expressive, lack of which has always been my main gripe with the language.
- tsmarsh 11y agoPoo. Nice analysis, btw.
- RandomBK 11y agoWrite code to be as clear and expressive as possible, then optimize for performance when you know performance is a problem. This is why I don't mind the performance cost of new language features like this. 90% of the time it won't matter, and you can optimize the other 10.
- haddr 11y agoSometimes it is the loop that brings most overhead to the processing time (imagine big array of ints, and some simple transformations). In such case refactoring would be easy, but anyway...
- userbinator 11y agoThat's the mindset that eventually makes everything slow and you can't easily see that 10% anymore because it gets more spread out the more abstractions you introduce. When loops stop looking like loops, it makes it rather harder to find them.
- vitalyd 11y agoAka "flat profile". Using these things in full force requires really banking on JIT doing the right thing (if perf is a concern), which is optimistic. A simple loop is just as readable as these one liners, and carries less risk of not being compiled as tightly.
- sacado2 11y agoThese constructs don't always make code any easier to understand / maintain. I have played a lot with NetBeans' feature that automatically translate loops with their "functional" equivalent. Sometimes the code was so clever I couldn't understand what was happening. Sure, code is more compact, but compactness is not an end in itself.
- justinhj 11y agoCompactness is more of a side effect and can be a detrimental one. Personally I find that this kind of code is more declarative of intent than the more imperative for loop. By using particular functional tools for the job you're avoiding any possibility of a bug in your looping construct and making your purpose explicit. I also like how it removes boiler plate to handle different container types making it easier to switch to different ones.
- Glide 11y agoThis reminds me a lot of Resharper's refactoring common things to LINQ in Visual Studio. A lot of times I just ran it thinking "oh wow that's a neat way to do it" and then reverted it back because it wasn't a common idiom or it made it much harder to understand. The same thing is going to happen with Java 8 until the feature becomes less shiny.
- zamalek 11y agoI'm really surprised that it isn't a zero cost abstraction, doesn't the Java JIT inline?
- Karunamon 11y agoI think there's some other secret sauce happening in there, especially since making the process parallelized is as easy as changing .stream to .parallelStream.
- pron 11y agoHotSpot (the OpenJDK JVM) does, and it should in this case, too, but this usage suffers from "the inlining problem"[1] and/or the profile pollution problem[2]. These are problems that are continuously addressed and improved with each release, but have not yet been satisfactorily resolved. [1]: http://www.azulsystems.com/blog/cliff/2011-04-04-fixing-the-inlining-problem http://www.azulsystems.com/blog/cliff/2011-04-04-fixing-the-... [2]: https://wiki.openjdk.java.net/display/HotSpot/MethodData https://wiki.openjdk.java.net/display/HotSpot/MethodData
- pjmlp 11y agoI think Graal does more aggressive inlining than Hotspot. Looking forward to the day it will be in the reference JDK.
- vitalyd 11y agoI don't think the 100M entries example suffers from inlining or profile pollution. It's just that the manual loop is going to be as tight as you can get it and it's likely the stream version leaves artifacts behind that are noticeable when the loop kernel is dead simple like this. -XX:UnlockDiagnosticVMOptions -XX:+PrintInlining will tell you whether this inlined, and dumping the JIT asm can be done to see what was actually generated.
- pron 11y agoDid you see that they're planning on making Graal a plugin of the standard OpenJDK build in Java 9? It's right there: http://openjdk.java.net/jeps/243 http://openjdk.java.net/jeps/243
- Gurkenmaster 11y ago>(Insert 100M trues and one false at the end, and find me the false) That sounds like arrays benefited heavily from the easy branch prediction.
- the8472 11y agothat's not a good benchmark, it doesn't take JIT warmup into account. Should have used JMH instead. http://openjdk.java.net/projects/code-tools/jmh/ http://openjdk.java.net/projects/code-tools/jmh/
- potatosareok 11y agoEchoing everyone else, Java microbenchmarking is annoying. You should proably use Caliper or whatever anyone else posts. Of course since I've never used any of them, here's my terrible example with stream beating for loop. https://gist.github.com/anonymous/2395fb0728e491bc54f5 https://gist.github.com/anonymous/2395fb0728e491bc54f5 Warmup Warming up done 9262288 # for loop 6156414 # stream Done edit: tuning WARMUP_RUNS to something lower (like 500 vs 10k) and for loop wins consistently vs warmup runs at 10k. If I put on -XX:+PrintCompilation I see some extra compilation output but I don't really understand the output. I assume some of this contributes but there's way more output then I expected tbh 4929 263 3 java.lang.invoke.LambdaForm$DMH/1581781576::invokeStatic_L_L (14 bytes) made not entrant 4929 339 4 java.util.function.Predicate::isEqual (20 bytes)
- vitalyd 11y agoYes your example is bad. If JIT inlines through, the code is trivially dead and can be removed. So yes, please use proper bench harness (JMH).
- potatosareok 11y agoYa I thought it might mark it as dead but even if I append the result to something like a List and print the list at the end (so it can't just not run the code?), Stream wins. Anyway I'm off to work for today, maybe I'll post in evening. edit: https://gist.github.com/anonymous/ed0d8f4a5c6553fe8435 https://gist.github.com/anonymous/ed0d8f4a5c6553fe8435 Is there some way in this example it could not actually run the code here?
- vitalyd 11y agoWell, you're not really testing for loops since 5there are other artifacts here: 1) forEach driver method is receiving multiple types, it's not monomorphic 2) you may be hitting OSR compilations 3) for loop may hit range checks on each get() 4) for loop version warms the cache for the stream version and this benchmark is mem ref heavy So, please try to use JMH to get more accurate picture. And, as mentioned, this isn't really testing for loop vs streams.
- laichzeit0 11y agoIf you want new language constructs, why bother writing 90% of your Java code in pure Java to begin with anyway? Use something that compiles to bytecode like Groovy. Profile your app, and write whatever is performance critical in pure Java.
- vorg 11y agoYou chose a scripting language as your example. The profiler would probably tell you to rewrite the entire app in Java, which can be tricky because although the Groovy code uses the Java syntax, the semantics are often different. If you use a language for building systems, the profiler would probably tell you nothing needs rewriting.