2014-01-27 207 views
4

我喜歡Scala的理解力以及它們與任何帶有地圖和flatMap的monadic類型的整合方式。但是,我也想做簡單的整數循環,而不會造成主要的速度損失。爲什麼Scala沒有以下兩個邏輯上相同的循環運行時具有相似的運行時性能,甚至編譯成相似的字節碼?爲什麼Scala for循環比邏輯上的循環慢?

// This is slow... 
for (i <- 0 until n) println(s"for loop with $i") 

// This runs much faster. It runs roughly at the same speed as Java code doing an identical while or for loop. 
var i = 0; 
while (i < n) { 
    println(s"while loop with $i") 
    i += 1 
} 
+0

確認一下...你*的*與編譯'-optimize',不是嗎? –

+0

@KevinWright:我很難找到'-optimize'選項實際所做的任何描述。你有沒有指向任何描述其效果的指針? –

+0

@RandallSchulz它隨着版本的不同而不同,但您可以在這裏得到一個公平的想法:http://magarciaepfl.github.io/scala/ –

回答

4

爲什麼他們不同的主要(但不僅僅是)拳擊。

在代碼:

for (i <- 0 until n) println(s"for loop with $i") 

你傳遞一個匿名函數println(s"for loop with $i")入修真​​。這相當於:

(0 until n) foreach (i => 
    println(s"for loop with $i") 
} 

這個功能是在字節碼,這意味着i不能是原始int抹去,它必須被裝箱爲Integer代替。 Java沒有Fixnum引用來避免這種代價,就像例如Smalltalk確實(尤其令人沮喪的是,給定了多少舊的Smalltalk!)

使用-optimize可以幫助在某些情況下,尤其是在scalac的主幹構建中。

您還可以使用scalaxy/loops加快速度:)

+0

使用-optimize選項時要小心,因爲某些庫遇到問題。我相信阿卡就是其中之一。 –

+0

這是信息豐富的,但它仍然只是一個藉口。 Scala編譯器可以清楚地解決這個問題。對整數進行裝箱整理對於編譯器實現來說可能更簡單和更清晰,但是沒有理由字節碼不能使用未裝箱的Java原始整數。 – user2684301

+1

@ user2684301這將是一個跨項目優化,目前穩定的編譯器不會這樣做。 'Range.foreach'需要一個* Generic *函數,因此優化需要將'foreach'的實現從標準庫複製到使用的代碼中,然後進行專門化 - 這會爲二進制兼容性帶來各種樂趣。 –