Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Array.slice so (shockingly!) slow?

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???

like image 389
Dima Avatar asked Jun 22 '16 13:06

Dima


People also ask

Is array slice slow?

slice() can be more than 2x slower than just manually copying elements from one array to another.

Is array slice fast?

Overview. The ArraySlice type makes it fast and efficient for you to perform operations on sections of a larger array.

Which are the three important parameters of array slicing?

Array Slicing in Python With two parameters Again, specifying any two parameters among the start, stop and end, you can perform array slicing in Python by considering default value for the third parameter.

What is the purpose of array slicing?

Array.prototype.slice() The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end ( end not included) where start and end represent the index of items in that array. The original array will not be modified.


1 Answers

It has been fixed in scala 2.12.

like image 84
Zang MingJie Avatar answered Oct 19 '22 07:10

Zang MingJie