这是我的基准代码:
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
我得到的结果是每秒 16 到 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
这对我来说每秒大约可以处理 5-600 万个切片。但这:
import scala.collections.JavaConversions._
(1 to 20).map { _ => bm(1000) { seq.subList(100,200) } }.sum / 20
数量在 12 到 1500 万之间! 当然,这不是数量级的差异,就像在数组的情况下一样,但是(1)这里没有涉及对原语的特殊处理,因此仅使用 java 标准工具实现这将是完全微不足道的,并且(2)集合是不可变的...返回对一系列索引的引用有多难???
最佳答案
已修复 scala 2.12 .
关于arrays - 为什么 Array.slice 如此(令人震惊!)慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37969193/