2016-11-28 23 views
1

的我是一个新手在Scala,当我试图分析我的Scala代码YourKit,我有关于array.drop使用一些惊人的发现。出奇的慢mutable.array.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% 

这对我来说真的很令人惊讶,是否有任何内部存储器复制会减慢处理速度?如果是这样,优化我的简单代码片段的最佳做法是什么?谢谢。

+2

'split'返回'Array'。对于'Array'。 'drop'必须复制。 –

回答

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 
+0

非常感谢您的帮助!我真的很感激! – computereasy

+0

@computereasy欢迎您,希望它有所帮助。确保正确测试它(如果你决定使用扩展到数组)。 –