簡體   English   中英

具有功能構造的Scala性能

[英]Scala performance with functional constructs

我目前正在分析用Scala編寫的應用程序的性能,我想知道是否可以使用功能結構。 一方面,我喜歡功能性編程的優雅和簡潔,另一方面,我害怕最終的表現。 我發現了一個特別好的例子。

我有一個包含一百萬個字符的字符串,我需要對每個數字求和。 典型的功能方法是這樣的:

val sum = value.map(_.asDigit).sum.toString

然而,這種美觀,簡潔,功能性的方法需要0.98秒(幾乎一秒鍾)

var sum = 0;

for(digit <- value)
  sum += digit.asDigit

另一方面,這種必要的方法僅需0.022秒(上述時間的2.​​24%) - 速度提高約50倍......

我確信問題出現了,因為Scala在第一種方法中生成一個新列表,然后再次迭代該列表以創建總和。

依靠功能結構是一個壞主意嗎? 我的意思是,它們很美 - 我愛它們 - 但它們慢了50倍......

PS我也嘗試了別的東西。

val sum = value.foldLeft(0)((sum, value) => sum + value.asDigit)

這種功能性方法雖然簡潔,可能比命令式方法更難閱讀,但需要0.085秒。 它更難閱讀,仍然慢4倍......

首先:你確定你已經對兩個版本進行了適當的基准測試嗎? 僅使用System.nanoTime等測量執行時間不會給出確切的結果。 查看JVM性能大師AlekseyShipilёv撰寫的這篇有趣且富有洞察力的博客文章

以下是使用優秀的Thyme scala基准測試庫的基准測試:

val value = "1234567890" * 100000
def sumf = value.map(_.asDigit).sum
def sumi = { var sum = 0; for(digit <- value) sum += digit.asDigit; sum }

val th = ichi.bench.Thyme.warmed(verbose = println)
scala> th.pbenchOffWarm("Functional vs. Imperative")(th.Warm(sumf))(th.Warm(sumi))
Benchmark comparison (in 6.654 s): Functional vs. Imperative
Significantly different (p ~= 0)
  Time ratio:    0.36877   95% CI 0.36625 - 0.37129   (n=20)
    First     40.25 ms   95% CI 40.15 ms - 40.34 ms
    Second    14.84 ms   95% CI 14.75 ms - 14.94 ms
res3: Int = 4500000

所以,是的,當務之急版本速度更快 但並不像你衡量的那么多。 在許多情況下,性能差異將完全無關緊要。 對於那些性能差異很重要的情況,scala讓您有機會編寫命令式代碼。 總而言之,我認為scala做得很好。

順便說一下:當你進行適當的基准測試時,你的第二種方法幾乎與命令式版本一樣快:

def sumf2 = value.foldLeft(0)(_ + _.asDigit)

scala> th.pbenchOffWarm("Functional2 vs. Imperative")(th.Warm(sumf2))(th.Warm(sumi))
Benchmark comparison (in 3.886 s): Functional2 vs. Imperative
Significantly different (p ~= 0)
  Time ratio:    0.89560   95% CI 0.88823 - 0.90297   (n=20)
    First     16.95 ms   95% CI 16.85 ms - 17.04 ms
    Second    15.18 ms   95% CI 15.08 ms - 15.27 ms
res17: Int = 4500000

由於來自@Odomontois的建議而更新:請注意,如果您真的想要優化它,則必須確保字符串的字符不被加框。 這是一個命令式的版本,看起來不是很好,但也幾乎盡可能快。 這是使用spire中的cfor宏,但while循環也可以正常工作。

def sumi3 = {
  var sum = 0
  cfor(0)(_ < value.length, _ + 1) { i => 
    sum += value(i).asDigit
  }
  sum
}

scala> th.pbenchOffWarm("Imperative vs. optimized Imperative")(th.Warm(sumi))(th.Warm(sumi3))
Benchmark comparison (in 4.401 s): Imperative vs. optimized Imperative
Significantly different (p ~= 0)
  Time ratio:    0.08925   95% CI 0.08880 - 0.08970   (n=20)
    First     15.10 ms   95% CI 15.04 ms - 15.16 ms
    Second    1.348 ms   95% CI 1.344 ms - 1.351 ms
res9: Int = 4500000

過早優化免責聲明:

除非你絕對確定a)一段代碼是性能瓶頸而且b)命令式版本要快得多,否則我總是更喜歡最可讀的版本。 Scala 2.12將帶有一個新的優化器 ,它將使功能樣式的大量開銷小得多,因為它可以在許多情況下進行高級優化,例如閉合內聯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM