自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Scala循環(huán)性能問題,為了性能,你愿意犧牲代碼的可維護性么?

開發(fā) 后端
最近我在學習我們產(chǎn)品的代碼,看到了一段代碼,我當時很是疑惑:為什么不用循環(huán)呢?于是就報了一個Issue,心想這樣寫可能有它的道理,但是需要澄清一下。

最近我在學習我們產(chǎn)品的代碼,看到了類似以下的一段代碼:

  1. x.set(1)  
  2. x.set(2)  
  3. x.set(3) 
  4. x.set(4) 
  5. x.set(5) 

我當時很是疑惑,為什么不用循環(huán)呢?于是就報了一個Issue,心想這樣寫可能有它的道理,但是需要澄清一下。

[[323560]]

另一個問題,就是我發(fā)現(xiàn)代碼里對循環(huán)的使用,各有不同的方式,有人寫array.foreach(f=>_),有人用使用index的for loop,個人覺得使用foreach的代碼比較簡潔,于是我也報了Issue,看看是不是應該使用簡潔的方式來寫循環(huán)。舉例:

for loop

  1. var index = 0 
  2. var arr = Array[String] 
  3. var length = arr.length  
  4. for ( index <- 0 to length ) { 
  5.     do() 

for each

  1. var index = 0 
  2. var arr = Array[String] 
  3. var length = arr.length  
  4. for ( index <- 0 to length ) { 
  5.     do() 

明顯foreach的版本要省不少代碼。

后來和我們的工程師溝通了一下,原來我們是為了性能優(yōu)化了代碼,因為for loop比foreach的性能好,所以我們采用稍微繁瑣的for loop。至于某些代碼中的foreach是因為遺留的還沒有來得及改動。

Scala的循環(huán)就行性能如何呢?我還是測試一下再說吧。

先看看不同的循環(huán)用法,我這里測試了四種,分別是 while loop,for loop,使用range的foreach, 和使用函數(shù)的foreach。

測試代碼如下:

  1. package profiling 
  2.  
  3. object Loop { 
  4.  
  5.   def whileLoop(arr:Array[Int]): Unit = { 
  6.     var idx = 0 
  7.     var n = arr.length 
  8.     val tStart = System.currentTimeMillis() 
  9.     while (idx < n) { 
  10.       arr(idx) = 1 
  11.       idx += 1 
  12.     } 
  13.     val tEnd = System.currentTimeMillis() 
  14.     println("while loop took " + (tEnd - tStart) + "ms") 
  15.   } 
  16.  
  17.   def forLoop(arr:Array[Int]): Unit = { 
  18.     var idx = 0 
  19.     var n = arr.length 
  20.     val tStart = System.currentTimeMillis() 
  21.     for(idx <- 0 until n) { 
  22.       arr(idx) = 1 
  23.     } 
  24.     val tEnd = System.currentTimeMillis() 
  25.     println("for loop took " + (tEnd - tStart) + "ms") 
  26.   } 
  27.  
  28.   def foreachLoop(arr:Array[Int]): Unit = { 
  29.     var n = arr.length 
  30.     val tStart = System.currentTimeMillis() 
  31.     (0 until n).foreach{idx => arr(idx) = 1} 
  32.     val tEnd = System.currentTimeMillis() 
  33.     println("foreach range took " + (tEnd - tStart) + "ms") 
  34.   } 
  35.  
  36.   def foreachFuncLoop(arr:Array[Int]): Unit = { 
  37.     val tStart = System.currentTimeMillis() 
  38.     arr.foreach{ idx => arr(idx) = 1} 
  39.     val tEnd = System.currentTimeMillis() 
  40.     println("foreach function took " + (tEnd - tStart) + "ms") 
  41.   } 
  42.  
  43.   def profileRun(n: Int) { 
  44.     val arr = new Array[Int](n) 
  45.  
  46.     whileLoop(arr) 
  47.     foreachLoop(arr) 
  48.     forLoop(arr) 
  49.     foreachFuncLoop(arr) 
  50.   } 
  51.  
  52.   def main(args:Array[String]) { 
  53.     profileRun(args(0).toInt) 
  54.   } 

我的環(huán)境是scala 2.13.1 , 調用500000000次的結果是:

Bash 代碼

  1. while loop took 344ms 
  2. foreach range took 484ms 
  3. for loop took 422ms 
  4. foreach function took 719ms 

可以看出,while loop是最快的,一般形式的foreach最慢,差不多是while loop的一倍。但是如果使用range的話,foreach循環(huán)也不算太慢。

那么為什么foreach會慢呢? 主要是foreach的函數(shù)調用帶來了額外的開銷。我們上面看到的數(shù)據(jù)其實是編譯器已經(jīng)優(yōu)化后的數(shù)字,如果我們把java的hotspot編譯選項關閉,(-Xint)再看看性能。

  1. while loop took 8548ms 
  2. foreach range took 39392ms 
  3. for loop took 40799ms 
  4. foreach function took 103489ms 

如果關閉JIT,foreach的性能要遠遠差于其他幾個選項。

對于循環(huán)的性能,我們可以得出這樣的結論:

  • 在正常打開JIT的情況下,foreach的性能大概比其他幾個選項慢一倍,其他幾個選項性能接近
  • 在關閉JIT優(yōu)化的情況下。foreach的性能要遠低于其他選項 (生產(chǎn)環(huán)境一般不考慮)

那么對于開頭講的不用循環(huán),直接重復代碼呢?我們也測試了一下:

  1. package profiling 
  2.  
  3. object Loop2Repeat { 
  4.   def whileLoop(): Unit = { 
  5.     var idx = 0 
  6.     var n = 5 
  7.     var x = 0 
  8.     while (idx < n) { 
  9.       x = idx 
  10.       idx += 1 
  11.     } 
  12.   } 
  13.  
  14.   def repeatLoop(): Unit = { 
  15.     var x = 0 
  16.     x = 1 
  17.     x = 2 
  18.     x = 3 
  19.     x = 4 
  20.     x = 5 
  21.   } 
  22.  
  23.   def test( f:()=>Unit, num: Int, name: String): Unit = { 
  24.     val tStart = System.currentTimeMillis() 
  25.     ( 0 until num).foreach{ _ => f} 
  26.     val tEnd = System.currentTimeMillis() 
  27.     println(name + " took " + (tEnd - tStart) + "ms") 
  28.   } 
  29.  
  30.   def main(args:Array[String]) { 
  31.     test(whileLoop, 50000000, "whileLoop") 
  32.     test(repeatLoop, 50000000, "repeatLoop") 
  33.   } 
  34.  

經(jīng)過50000000次循環(huán),數(shù)據(jù)如下:

  1. whileLoop took 281ms 
  2. repeatLoop took 47ms 

確實,因為循環(huán)控制的邏輯帶來的額外開銷,比簡單的重復代碼性能下降了不少。

 

責任編輯:趙寧寧 來源: 今日頭條
相關推薦

2012-07-18 10:47:49

Java

2024-10-30 08:08:45

2023-10-16 09:30:06

Java代碼

2020-04-28 16:12:50

前端JavaScript代碼

2024-10-07 08:32:54

2017-05-27 15:43:50

Python深拷貝memo

2023-10-17 09:19:34

開發(fā)Java

2021-12-29 10:30:15

JMH代碼Java

2025-02-13 00:28:26

2024-04-07 10:13:57

C++代碼if-else

2023-04-28 14:54:57

架構開發(fā)React

2018-08-03 09:00:00

編程語言Python外部庫

2024-04-18 08:39:57

依賴注入控制反轉WPF

2022-12-15 11:04:12

數(shù)字化轉型循環(huán)性

2020-10-13 14:52:48

物聯(lián)網(wǎng)

2023-09-20 23:03:40

C++函數(shù)

2016-10-25 15:34:00

VDI性能監(jiān)控

2021-03-21 23:43:22

線程編程安全

2024-02-26 08:33:51

并發(fā)編程活躍性安全性

2021-03-18 07:52:42

代碼性能技巧開發(fā)
點贊
收藏

51CTO技術棧公眾號