这是我的基准代码:
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
多次运行,我总是得到每秒大约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
我得到的结果是每秒1600到1800万个切片。 超过10次。
现在,我知道scala在提供功能习惯和类型安全方面所取得的权衡的所有通常的推理有时以牺牲性能为代价......
但在这种情况下,我认为他们都没有回答一个简单的问题:为什么ArrayOps.slice
没有以这种方式实现 ???我知道,需要多个相同的实现,因为java处理原始数组的方式,但这最多只是一个小麻烦,而不是真正的破坏性问题,以证明10倍的性能损失。< / p>
.slice
只是一个例子,大多数其他数组操作似乎也遇到同样的问题。为什么必须这样?
更新现在,我发现更令人震惊:
val seq = new scala.util.Random().alphanumeric.take(1000).toIndexedSeq
(1 to 20).map { _ => bm(1000) { seq.slice(100,200) } }.sum / 20
这对我来说每秒约有5-6百万片。但是这个:
import scala.collections.JavaConversions._
(1 to 20).map { _ => bm(1000) { seq.subList(100,200) } }.sum / 20
在12到1500万之间! 当然,这不是数量级的差异,就像在数组的情况下,但是(1)这里没有涉及原语的特殊处理,所以这对于使用java标准工具实现完全是微不足道的,以及(2)集合是不可变的...... 返回对一系列索引的引用有多难 ???