簡體   English   中英

GHC優化

[英]GHC optimization

下面的兩個Haskell函數似乎不同,索引變量是隱式還是顯式,但性能差異是兩個數量級。

此函數大約需要0.03秒來計算mfib 30:

let mfib = (map fib [0..] !!)
  where
    fib 0 = 0
    fib 1 = 1
    fib x = mfib (x-1) + mfib (x-2)

對於mfib 30,此功能大約需要3秒鍾:

let mfib i = map fib [0..] !! i
  where
    fib 0 = 0
    fib 1 = 1
    fib x = mfib (x-1) + mfib (x-2)

我猜它與GHC內聯規則有關,並且一直在嘗試添加內聯/無內置編譯指示以獲得匹配的性能。

編輯:我理解如何查找惰性列表可以用來記憶fib函數以及為什么傳統的fib定義非常慢。 我期待memoization在第二個函數以及第一個函數中工作,並且不理解為什么它不是。

在查看desugared代碼時,更容易理解這些差異,因此這里有兩個函數的部分脫落版本。

let mfib = let fib 0 = 0
               fib 1 = 1
               fib x = mfib (x-1) + mfib (x-2)
           in (!!) (map fib [0..])

let mfib = \i ->
               let fib 0 = 0
                   fib 1 = 1
                   fib x = mfib (x-1) + mfib (x-2)
               in map fib [0..] !! i

請注意,在第二個程序中,表達式map fib [0..]出現在\\i -> ... ,因此它(通常,沒有優化)將針對i每個值進行評估。 請參閱GHC Haskell中何時自動記憶?

不,這與內聯無關。 區別在於mfib = (map fib [0..] !!)沒有參數。 它當然仍然是一個函數,但是預先評估該函數不需要傳遞任何參數。 特別是,評估此mfib將以一種可以為所有索引重用的方式生成fib列表。

OTOH, mfib i = map fib [0..] !! i mfib i = map fib [0..] !! i意思是當你實際傳遞一個參數i時,只會考慮整個where塊。

如果您反復多次評估函數,則兩者僅相同。 不幸的是,對於第二個版本,函數自己的遞歸已經一次又一次地調用它! 所以mfib (x-1) + mfib (x-2)然后需要完成mfib (x-1)的整個工作, 然后再完成mfib (x-2)的全部工作。 所以mfib n花費兩倍以上的計算成本mfib (n-1)因此mfib ∈O(2 N)。

這是非常浪費的,因為mfib (x-2)中的大部分術語也已經在mfib (x-1)並且可以簡單地重復使用。 嗯,這正是你的第一個版本所做的,因為它計算一次和所有索引的fib列表,所以評估mfib (x-1)已經完成了大部分工作,然后可以簡單地由mfib (x-2)重新讀取mfib (x-2) ,將復雜度降低到多項式。

暫無
暫無

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

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