您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么理解Scala循環性能問題”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么理解Scala循環性能問題”吧!
最近我在學習我們產品的代碼,看到了類似以下的一段代碼:
x.set(1) x.set(2) x.set(3) x.set(4) x.set(5)
我當時很是疑惑,為什么不用循環呢?于是就報了一個Issue,心想這樣寫可能有它的道理,但是需要澄清一下。
另一個問題,就是我發現代碼里對循環的使用,各有不同的方式,有人寫array.foreach(f=>_),有人用使用index的for loop,個人覺得使用foreach的代碼比較簡潔,于是我也報了Issue,看看是不是應該使用簡潔的方式來寫循環。舉例:
for loop
var index = 0 var arr = Array[String] var length = arr.length for ( index <- 0 to length ) { do() }
for each
var index = 0 var arr = Array[String] var length = arr.length for ( index <- 0 to length ) { do() }
明顯foreach的版本要省不少代碼。
后來和我們的工程師溝通了一下,原來我們是為了性能優化了代碼,因為for loop比foreach的性能好,所以我們采用稍微繁瑣的for loop。至于某些代碼中的foreach是因為遺留的還沒有來得及改動。
Scala的循環就行性能如何呢?我還是測試一下再說吧。
先看看不同的循環用法,我這里測試了四種,分別是 while loop,for loop,使用range的foreach, 和使用函數的foreach。
測試代碼如下:
package profiling object Loop { def whileLoop(arr:Array[Int]): Unit = { var idx = 0 var n = arr.length val tStart = System.currentTimeMillis() while (idx < n) { arr(idx) = 1 idx += 1 } val tEnd = System.currentTimeMillis() println("while loop took " + (tEnd - tStart) + "ms") } def forLoop(arr:Array[Int]): Unit = { var idx = 0 var n = arr.length val tStart = System.currentTimeMillis() for(idx <- 0 until n) { arr(idx) = 1 } val tEnd = System.currentTimeMillis() println("for loop took " + (tEnd - tStart) + "ms") } def foreachLoop(arr:Array[Int]): Unit = { var n = arr.length val tStart = System.currentTimeMillis() (0 until n).foreach{idx => arr(idx) = 1} val tEnd = System.currentTimeMillis() println("foreach range took " + (tEnd - tStart) + "ms") } def foreachFuncLoop(arr:Array[Int]): Unit = { val tStart = System.currentTimeMillis() arr.foreach{ idx => arr(idx) = 1} val tEnd = System.currentTimeMillis() println("foreach function took " + (tEnd - tStart) + "ms") } def profileRun(n: Int) { val arr = new Array[Int](n) whileLoop(arr) foreachLoop(arr) forLoop(arr) foreachFuncLoop(arr) } def main(args:Array[String]) { profileRun(args(0).toInt) } }
我的環境是scala 2.13.1 , 調用500000000次的結果是:
Bash 代碼
while loop took 344ms foreach range took 484ms for loop took 422ms foreach function took 719ms
可以看出,while loop是最快的,一般形式的foreach最慢,差不多是while loop的一倍。但是如果使用range的話,foreach循環也不算太慢。
那么為什么foreach會慢呢? 主要是foreach的函數調用帶來了額外的開銷。我們上面看到的數據其實是編譯器已經優化后的數字,如果我們把java的hotspot編譯選項關閉,(-Xint)再看看性能。
while loop took 8548ms foreach range took 39392ms for loop took 40799ms foreach function took 103489ms
如果關閉JIT,foreach的性能要遠遠差于其他幾個選項。
對于循環的性能,我們可以得出這樣的結論:
在正常打開JIT的情況下,foreach的性能大概比其他幾個選項慢一倍,其他幾個選項性能接近
在關閉JIT優化的情況下。foreach的性能要遠低于其他選項 (生產環境一般不考慮)
那么對于開頭講的不用循環,直接重復代碼呢?我們也測試了一下:
package profiling object Loop2Repeat { def whileLoop(): Unit = { var idx = 0 var n = 5 var x = 0 while (idx < n) { x = idx idx += 1 } } def repeatLoop(): Unit = { var x = 0 x = 1 x = 2 x = 3 x = 4 x = 5 } def test( f:()=>Unit, num: Int, name: String): Unit = { val tStart = System.currentTimeMillis() ( 0 until num).foreach{ _ => f} val tEnd = System.currentTimeMillis() println(name + " took " + (tEnd - tStart) + "ms") } def main(args:Array[String]) { test(whileLoop, 50000000, "whileLoop") test(repeatLoop, 50000000, "repeatLoop") } }
經過50000000次循環,數據如下:
whileLoop took 281ms repeatLoop took 47ms
確實,因為循環控制的邏輯帶來的額外開銷,比簡單的重復代碼性能下降了不少。
感謝各位的閱讀,以上就是“怎么理解Scala循環性能問題”的內容了,經過本文的學習后,相信大家對怎么理解Scala循環性能問題這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。