1

我是. Scala_ _ScalaYourKitarray.drop

这是我写的:

...
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%

这真的让我感到惊讶,是否有任何内部存储器副本会减慢处理速度?如果是这样,优化我的简单代码片段的最佳做法是什么?谢谢你。

4

1 回答 1

2

这真的让我感到惊讶,是否有任何内部存储器副本会减慢处理速度?

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
于 2016-11-28T07:23:42.143 回答