这篇文章主要讲解了“怎么理解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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。