为什么Array.slice如此(令人震惊!)慢?

Dim*_*ima 27 arrays performance scala

这是我的基准代码:

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
Run Code Online (Sandbox Code Playgroud)

运行这几次,我总是得到每秒大约150万片的数字.介于1.4和1.6之间.

现在,我这样做:

 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
Run Code Online (Sandbox Code Playgroud)

我得到的结果是每秒1600到1800万个切片.这速度提高了10倍以上.

现在,我知道scala在提供功能习惯和类型安全方面所取得的权衡的所有通常的推理有时以牺牲性能为代价......但在这种情况下,我认为它们都没有回答一个简单的问题:为什么ArrayOps.slice没有这样实现 ??? 我知道,需要多个相同的实现,因为java处理原始数组的方式,但这最多只是一个小麻烦,而不是真正的破坏性问题,以证明10倍的性能损失.

.slice只是一个例子,大多数其他阵列操作似乎也遇到了同样的问题.为什么必须这样?

现在更新,我觉得这里更令人震惊:

val seq = new scala.util.Random().alphanumeric.take(1000).toIndexedSeq
(1 to 20).map { _ => bm(1000) { seq.slice(100,200) } }.sum / 20
Run Code Online (Sandbox Code Playgroud)

这对我来说每秒大约有5-6百万个切片.但是这个:

import scala.collections.JavaConversions._
(1 to 20).map { _ => bm(1000) { seq.subList(100,200) } }.sum / 20
Run Code Online (Sandbox Code Playgroud)

在12到1500万之间!当然,这是不是幅度差的顺序,像在阵列情况,但(1)存在这里涉及原语的任何特殊处理,因此这将是完全微不足道的,只是实现使用Java标准工装,和(2)的集合是不可变的... 返回对一系列索引的引用有多难

Zan*_*Jie 2

它已在scala 2.12中修复。

  • 是的...由我:D (22认同)