2

I recently started comparing Java vs Kotlin performance and after fisrt try I already found some strange things.

I run a JHM benchmark on reducing a stream/list, as expected Java on stream was faster, but I noticed something else.

Stream<Long>.reduce(0L, (a, b) -> a + b);

Was a significantly slower than:

Stream<Long>.reduce( (a, b) -> a + b).orElse(0L);

The stream size was ~50k elements and input was the same (code can be found here: https://github.com/mariusz-zawadzki/kotlin-vs-java-jmh )

So my question is: can anyone point me to what happened in java 9 that improved the performance?

edit:

After looking into that a bit more I found no differences in other types of operations, only in Long. After chaning map(Long::valueOf) to mapToLong(Long::valueOf) there were no difference between java 8 and 9 in performance, nor there were any difference in reduce with or witout acumulator.

So my current guess is: there is an optimization in java 9 that somehow changes/knows how to change Stram to LongStream or get's rid of boxing/unboxing there. Conclusion: Stream.reduce(BinaryOperator) has the same performance as LongStream.reduce but only in java 9.

Java 9:
MyBenchmark.testJava                  thrpt  200  1234,795 ? 13,398  ops/s
MyBenchmark.testJavaAcumulator        thrpt  200  1234,981 ?  1,994  ops/s
MyBenchmark.testJavaObject            thrpt  200  1216,849 ?  6,028  ops/s
MyBenchmark.testJavaObjectAcumulator  thrpt  200   933,023 ?  5,864  ops/s
Java 8
Benchmark                              Mode  Cnt     Score   Error  Units
MyBenchmark.testJava                  thrpt  200  1199,363 ? 3,870  ops/s
MyBenchmark.testJavaAcumulator        thrpt  200  1195,621 ? 9,716  ops/s
MyBenchmark.testJavaObject            thrpt  200   986,778 ? 4,852  ops/s
MyBenchmark.testJavaObjectAcumulator  thrpt  200   993,045 ? 2,600  ops/s
Mariusz Zawadzki
  • 479
  • 1
  • 3
  • 13
  • 4
    I'm confused. Are you comparing two `reduce()` overloads? Two versions of Java? Java and Kotlin? – shmosel Mar 12 '18 at 19:49
  • First of all, use a `LongStream`. This avoids boxing/unboxing. – Johannes Kuhn Mar 12 '18 at 19:50
  • @shmosel: Yes I'm comparing Stream. Optional reduce(BinaryOperator accumulator); vs: T reduce(T identity, BinaryOperator accumulator); – Mariusz Zawadzki Mar 12 '18 at 19:51
  • I think your question is replied at https://stackoverflow.com/questions/46615484/why-does-this-code-using-streams-run-so-much-faster-in-java-9-than-java-8 – carlosvin Mar 12 '18 at 19:52
  • Then what does Kotlin or Java 9 have to do with it? – shmosel Mar 12 '18 at 19:53
  • @shmosel: Java 9 because I see difference only in Java 9 not java 8. Kotlin vs java was my initial goal – Mariusz Zawadzki Mar 12 '18 at 19:56
  • @carlosvin: maybe, I will look if this is not a problem with the way I benchmark. – Mariusz Zawadzki Mar 12 '18 at 19:56
  • @JohannesKuhn: I started with a list, then filtered it then end up with Stream I would use LongStream or IntStream if it was my starting data structure, but I wanted to compare filtering, mapping and reduction on kotlin List vs Java Stream, but ended up with my finding. – Mariusz Zawadzki Mar 12 '18 at 19:56
  • nothing code wise AFAIK, but the VM has changed, it could be numerous things that are different around tiered compilation. You should just be happy it's not the other way around *or* really look at your tests and the correctness of those – Eugene Mar 12 '18 at 20:35
  • @Eugene: I did the same with "Strings" and see no difference there, so it might be some improvement on arithmetic operations, but the strange this is that code doing just this: public void accept(T t) { state = reducer.apply(state, t); } is slower than code doing this: public void accept(T t) { if (empty) { empty = false; state = t; } else { state = operator.apply(state, t); } } – Mariusz Zawadzki Mar 12 '18 at 20:38
  • @Eugene: and btw. I'm happy that it works better in more recent version of Java. But Still I'm curious WHY? – Mariusz Zawadzki Mar 12 '18 at 20:41

0 Answers0