mutable.array.drop 出奇的慢
Surprisingly slow of mutable.array.drop
我是 Scala
的新手,当我尝试使用 YourKit
分析我的 Scala
代码时,我对 [=16= 的用法有一些令人惊讶的发现].
这是我写的:
...
val items = s.split(" +") // s is a string
...
val s1 = items.drop(2).mkString(" ")
...
在我的代码 运行 的 1 分钟内,YourKit 告诉我函数调用 items.drop(2)
大约占总执行时间的 11%。
Lexer.scala:33 scala.collection.mutable.ArrayOps$ofRef.drop(int) 1054 11%
这真是让我感到惊讶,是否有任何内存副本会减慢处理速度?如果是这样,优化我的简单代码片段的最佳做法是什么?谢谢。
This is really surprising to me, is there any internal memory copy
that slow down the processing?
ArrayOps.drop
内部调用 IterableLike.slice
,它分配一个构建器,为每次调用生成一个新的 Array
:
override def slice(from: Int, until: Int): Repr = {
val lo = math.max(from, 0)
val hi = math.min(math.max(until, 0), length)
val elems = math.max(hi - lo, 0)
val b = newBuilder
b.sizeHint(elems)
var i = lo
while (i < hi) {
b += self(i)
i += 1
}
b.result()
}
您看到的是迭代 + 分配的成本。您没有指定这种情况发生了多少次以及集合的大小,但如果集合很大,这可能会很耗时。
优化它的一种方法是生成一个 List[String]
而不是简单地迭代集合并删除它的 head
元素。请注意,这将额外遍历 Array[T]
以创建列表,因此请确保对其进行基准测试以查看您是否真正获得了任何东西:
val items = s.split(" +").toList
val afterDrop = items.drop(2).mkString(" ")
另一种可能性是丰富 Array[T]
以包括您自己的 mkString
版本,它手动填充 StringBuilder
:
object RichOps {
implicit class RichArray[T](val arr: Array[T]) extends AnyVal {
def mkStringWithIndex(start: Int, end: Int, separator: String): String = {
var idx = start
val stringBuilder = new StringBuilder(end - start)
while (idx < end) {
stringBuilder.append(arr(idx))
if (idx != end - 1) {
stringBuilder.append(separator)
}
idx += 1
}
stringBuilder.toString()
}
}
}
现在我们有:
object Test {
def main(args: Array[String]): Unit = {
import RichOps._
val items = "hello everyone and welcome".split(" ")
println(items.mkStringWithIndex(2, items.length, " "))
}
产量:
and welcome
我是 Scala
的新手,当我尝试使用 YourKit
分析我的 Scala
代码时,我对 [=16= 的用法有一些令人惊讶的发现].
这是我写的:
...
val items = s.split(" +") // s is a string
...
val s1 = items.drop(2).mkString(" ")
...
在我的代码 运行 的 1 分钟内,YourKit 告诉我函数调用 items.drop(2)
大约占总执行时间的 11%。
Lexer.scala:33 scala.collection.mutable.ArrayOps$ofRef.drop(int) 1054 11%
这真是让我感到惊讶,是否有任何内存副本会减慢处理速度?如果是这样,优化我的简单代码片段的最佳做法是什么?谢谢。
This is really surprising to me, is there any internal memory copy that slow down the processing?
ArrayOps.drop
内部调用 IterableLike.slice
,它分配一个构建器,为每次调用生成一个新的 Array
:
override def slice(from: Int, until: Int): Repr = {
val lo = math.max(from, 0)
val hi = math.min(math.max(until, 0), length)
val elems = math.max(hi - lo, 0)
val b = newBuilder
b.sizeHint(elems)
var i = lo
while (i < hi) {
b += self(i)
i += 1
}
b.result()
}
您看到的是迭代 + 分配的成本。您没有指定这种情况发生了多少次以及集合的大小,但如果集合很大,这可能会很耗时。
优化它的一种方法是生成一个 List[String]
而不是简单地迭代集合并删除它的 head
元素。请注意,这将额外遍历 Array[T]
以创建列表,因此请确保对其进行基准测试以查看您是否真正获得了任何东西:
val items = s.split(" +").toList
val afterDrop = items.drop(2).mkString(" ")
另一种可能性是丰富 Array[T]
以包括您自己的 mkString
版本,它手动填充 StringBuilder
:
object RichOps {
implicit class RichArray[T](val arr: Array[T]) extends AnyVal {
def mkStringWithIndex(start: Int, end: Int, separator: String): String = {
var idx = start
val stringBuilder = new StringBuilder(end - start)
while (idx < end) {
stringBuilder.append(arr(idx))
if (idx != end - 1) {
stringBuilder.append(separator)
}
idx += 1
}
stringBuilder.toString()
}
}
}
现在我们有:
object Test {
def main(args: Array[String]): Unit = {
import RichOps._
val items = "hello everyone and welcome".split(" ")
println(items.mkStringWithIndex(2, items.length, " "))
}
产量:
and welcome