<code>val ia = Array.range(0,sz)</code>
<code>ia.foldLeft(0)(_ + _)</code>
, and compared to the time using a plain old imperative while loop. Using foldLeft was a startling 33x slower than the while loop. Other stats of interest:
(These figures are on a Core 2 Duo, WiFi disabled, no browsers, and reflect hotspot performance with gc reset over 100 reps. StdDev < 1%.)
Of course many apps are not data intensive, and spend more time in IO or network operations, so even a 33x slowdown would not be noticed. For those data intensive apps where array performance does matter, the choice is between imperative style on few cores, or functional style to utilize many cores. This is an obstacle to the DRY principle.
What then is the reason for this disparity in functional versus imperative performance? I only looked in detail at foldLeft, but I suspect similar issues underly other collection methods. By slowly morphing the generic foldLeft code into a fast while loop, and examining javap disassembled code where a small change made a big speed difference, the culprit turns out to be a Function2 wrapper on the function literal passed to foldLeft.
In byte code, I clearly see primitive ints coming from the array, and the functional literal "_ + _" is compiled to a method <code>public final int apply(int, int)</code> that simply iadds the 2 ints. Unfortunately, that apply method is called by a Function2 apply method with the signature of <code>public final Object apply(Object, Object)</code>. Regardless of the types of the array or the "visitor" function literal, the compiler always creates a Function2 apply that forces boxing, both coming and going!
This even explained #4 above, in which summing an array of Integer objects is faster than summing an array of ints, because it means that one less int needs boxing before calling Function2.apply.
Despite my attention here to Array, I'm also interested in better performance for ArrayBuffer, ResizableArray and ParArray. Here's what I'd like to know:
I did see that Erik has an alternative to Scala collections, https://github.com/non/debox . Are these APIs considered stable? I also attended Paul's Valentines Day talk, http://www.youtube.com/watch?v=o2WtIroR7Ag&feature=youtu.be , where he mentioned the lack of specialization support for Arrays, since they don't know about ClassTags. (None of what I've said will be surprising to Paul, and I don't imply that there's an easy fix!)
Hope this sparks some productive discussion. - Bob K. (aka Kabob)
Thanks guys.
> Goodness--you haven't seen this mentioned at all? I guess it's all ended up buried in other discussions;
> at this point I was considering it so well known that it wasn't even worth mentioning.
Well yes I've seen discussions, but not specific numbers like 33x slower functional performance over imperative, and not identifying a single wrapper as the reason for the boxing/unboxing. Ultimately, I would like not to sacrifice functional parallelism for serial speed. Although there is always some overhead, 33x seems excessive. And I would like this integrated in the standard libs. And I think it's doable. The question is, at what effort?
> You can always write your own specialized versions of these methods and
> implicitly add them to Array, ArrayBuffer, etc. However, all the
> non-array collections (including ArrayBuffer) box as well, so as you
> observed specialized methods won't be of any benefit unless you create
> your own specialized versions of the collections themselves.
Actually I began my investigation of Array from the standpoint of creating fast replacements for ArrayBuffer and ResizableArray, without sacrificing their genericity or inheritance hierarchy. In this I actually succeeded. My replacements use primitive arrays "under the covers" if the underlying types are primitive. It was then that I belatedly saw that the underlying primitive arrays are not themselves functionally fast.
The goal of my post was actually to prompt us thinking about how to make the standard library fast (esp. regarding primitive arrays), yet without breaking the APIs. Like I said, "Regardless of the types of the array or the "visitor" function literal, the compiler always creates a Function2 apply [signature: (Object, Object) => Object] that forces boxing, both coming and going!" It seems like replacing this default wrapper with a Specialized-like solution would gain a lot of bang for the buck. If the concern is code bloat in the libraries, combinatorial explosion can at least be avoided for common cases like (Int, Int) => Int.
Martin expressed somewhere (can't find the link) a concern that performance is a factor in the adoption decision for a technology.
If the choice Scala offers developers is always to reinvent the wheel or suffer poor performance, Scala's detractors have a valid argument. As mentioned earlier, imperative Scala code can outperform Java (at least in my limited experiments), so I think there is an opportunity to actually flip the argument. Can the standard libs be the performance champs without sacrificing either functionality or genericity? This would be (yet another) major selling point for the language, and from what I've seen, it should be possible without a huge investment. I could of course be wrong about the investment, but that's where I'm looking for more knowledgeable opinions.
> Anyway, since 2.10 has come out there have been a lot of exciting
> developments. At this point we definitely have the tools to tackel the
> problem, although I don't expect a solution in the standard library
> soon.
Agreed, although I would like to lend my voice to goose the priority for the standard libs.
Thanks for your time talking about this! - Bob
Thanks guys.
> Goodness--you haven't seen this mentioned at all? I guess it's all ended up buried in other discussions;
> at this point I was considering it so well known that it wasn't even worth mentioning.
Well yes I've seen discussions, but not specific numbers like 33x slower functional performance over imperative
From: Rex Kerr <ich...@gmail.com> To: scala...@listes.epfl.ch On Sat, Jan 23, 2010 at 2:00 PM, Ismael Juma <mli...@juma.me.uk> wrote: > On Sat, 2010-01-23 at 13:54 -0500, Jonathan Shore wrote: > > Create a micro-benchmark suited to the JVM that compares a for > comprehension with -optimise (and possibly specialized) and a while loop > and show that the performance difference is still large. I'd already done something much like this when I was working on a fast vector library. For example, comparing: - while loop with primitives - custom IntRange class with dedicated foreach - regular Range (extends Projection[Int]) - for loop - reduceLeft gives the following answers $ scalac -optimise DoNotUseForOrReduce.scala $ scala DoNotUseForOrReduce Time (seconds): while=0.56 custom=4.81 foreach=4.67 for=6.13 reduce=13.82 Time (seconds): while=0.56 custom=4.72 foreach=4.67 for=6.13 reduce=13.97 Time (seconds): while=0.56 custom=4.66 foreach=4.73 for=6.15 reduce=14.14 Time (seconds): while=0.56 custom=4.65 foreach=4.71 for=6.13 reduce=14.11 Time (seconds): while=0.56 custom=4.69 foreach=4.61 for=6.14 reduce=14.16 (etc.) which shows that a bare while is 8-25x faster than anything else. That was for 2.7.3; 2.8.0.r20326 is a bit better:
, and not identifying a single wrapper as the reason for the boxing/unboxing
The goal of my post was actually to prompt us thinking about how to make the standard library fast (esp. regarding primitive arrays), yet without breaking the APIs. Like I said, "Regardless of the types of the array or the "visitor" function literal, the compiler always creates a Function2 apply [signature: (Object, Object) => Object] that forces boxing, both coming and going!" It seems like replacing this default wrapper with a Specialized-like solution would gain a lot of bang for the buck. If the concern is code bloat in the libraries, combinatorial explosion can at least be avoided for common cases like (Int, Int) => Int.
--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Bob--