Reputation: 40510
Here is my benchmark code:
def bm(duration: Long)(f: => Unit)={
val end = System.currentTimeMillis + duration
var count = 0
while(System.currentTimeMillis < end) { f; count += 1 }
count
}
val array = new scala.util.Random().alphanumeric.take(1000).toArray
(1 to 20).map { _ => bm(1000) { array.slice(100,200) } }.sum / 20
Running this several times, I consistently get numbers in the ballpark of about 1.5 million slices per second. Between 1.4 and 1.6.
Now, I do this:
implicit class FastSlicing(val a: Array[Char]) extends AnyVal {
def fastSlice(from: Int, until: Int) = Arrays.copyOfRange(a, from, until)
}
(1 to 20).map { _ => bm(1000) { array.fastSlice(100,200) } }.sum / 20
And the result I get is between 16 and 18 million of slices per second. This is more than 10 times faster.
Now, I know all the usual reasoning about the trade-offs that scala makes to provide functional idioms and type safety sometimes at the cost of performance ...
But in this case, I think they all fail to answer a simple question: why is ArrayOps.slice
not implemented this way??? I realize, there would be multiple identical implementations needed, because of the way java deals with primitive arrays, but that's at most a minor annoyance, not really a deal-breaker kind of problem to justify a 10x performance hit.
The .slice
is only one example, most of other array ops seem to suffer from the same problem too. Why does it have to be this way?
Update now, here is something that I find even more shocking:
val seq = new scala.util.Random().alphanumeric.take(1000).toIndexedSeq
(1 to 20).map { _ => bm(1000) { seq.slice(100,200) } }.sum / 20
This does about 5-6 million slices per second for me. But this:
import scala.collections.JavaConversions._
(1 to 20).map { _ => bm(1000) { seq.subList(100,200) } }.sum / 20
does between 12 and 15 million! Granted, this is not order of magnitude difference, like in the arrays case, but (1) there is no special handling of primitives involved here, so this would be completely trivial to just implement using java standard tooling, and (2) the collection is immutable ... how hard can it be to return a reference to a range of indices???
Upvotes: 27
Views: 1890