2016-11-28 21 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歡迎您,希望它有所幫助。確保正確測試它(如果你決定使用擴展到數組)。 –