2012-09-21 63 views
15

我對haskell有一定的理解,但我總是很少不確定應該使用什麼樣的編譯指示和優化,以及在哪裏。像何時使用各種語言編譯指示和優化?

  • 像什麼時候使用SPECIALIZE編譯指示和它有什麼樣的性能增益。
  • 使用地點RULES。我聽說有人採取特定的規則不開火?我們如何檢查?
  • 什麼時候使函數的參數嚴格,什麼時候可以提供幫助?我明白,嚴格論證會使參數被評估爲正常形式,那麼爲什麼我不應該對所有函數參數加嚴格?我如何決定?
  • 如何查看和檢查我的程序中是否有空間泄漏?什麼是構成空間泄漏的一般模式?
  • 我該如何看看是否有太多懶惰的問題?我總是可以檢查堆分析,但我想知道什麼是懶惰傷害的一般原因,示例和模式?

是否有任何關於高級優化(包括更高和更低級別)的源代碼,特別是對於haskell?

+0

RHW CH 25? http://book.realworldhaskell.org/read/profiling-and-optimization.html –

+0

@DonStewart謝謝..我已經看過RWH ..問題是我不知道他們做什麼,但我並沒有對當直覺以及他們的使用是重要的而不重要的地方。我問了這個問題找出其他來源,以進一步加深我的理解。 – Satvik

回答

18

像什麼時候使用SPECIALIZE編譯指示和它的性能增益。

如果您有一個(類型)多態函數,並且希望它經常在類的一個或幾個實例上調用,那麼讓編譯器專門化一個函數。

專業化將字典查找刪除使用的位置,並且通常可以進一步優化,類成員函數通常可以內聯,然後進行嚴格分析,兩者都可能帶來巨大的性能提升。如果唯一可能的優化是消除查詢,那麼收益一般不會很大。

由於GHC-7,它可能更有效地讓功能的{-# INLINABLE #-}編譯,這使得在接口文件中提供的(幾乎沒有變化,進行一些規範和脫糖)源,因此函數可以專門和甚至可能在呼叫站點內聯。

在哪裏可以使用RULES。我聽說有人採取特定的規則不開火?我們如何檢查?

您可以使用-ddump-rule-firings命令行選項來檢查哪些規則已被觸發。這通常會拋出大量已解除規則,因此您必須爲自己的規則搜索一下。

您使用規則

  • 當你有一個功能更有效的版本,特殊工種,如

    {-# RULES 
    "realToFrac/Float->Double" realToFrac = float2Double 
        #-} 
    
  • 當某些功能可以被更有效的版本替換爲特殊參數時,例如,

    {-# RULES 
    "^2/Int"  forall x. x^(2 :: Int) = let u = x in u*u 
    "^3/Int"  forall x. x^(3 :: Int) = let u = x in u*u*u 
    "^4/Int"  forall x. x^(4 :: Int) = let u = x in u*u*u*u 
    "^5/Int"  forall x. x^(5 :: Int) = let u = x in u*u*u*u*u 
    "^2/Integer" forall x. x^(2 :: Integer) = let u = x in u*u 
    "^3/Integer" forall x. x^(3 :: Integer) = let u = x in u*u*u 
    "^4/Integer" forall x. x^(4 :: Integer) = let u = x in u*u*u*u 
    "^5/Integer" forall x. x^(5 :: Integer) = let u = x in u*u*u*u*u 
        #-} 
    
  • 重寫根據一般規律可能會產生的代碼是更好地優化,例如表達式時

    {-# RULES 
    "map/map" forall f g. (map f) . (map g) = map (f . g) 
        #-} 
    
在後者的樣式

廣泛使用的RULES在融合框架製成,例如在text庫,並且該列表的功能在base中,不同種類的融合(foldr/build融合)的實施使用規則。

什麼時候使函數的參數嚴格,什麼時候可以提供幫助?我明白,嚴格論證會使參數被評估爲正常形式,那麼爲什麼我不應該對所有函數參數加嚴格?我如何決定?

製作一個參數嚴格將確保其被評估到弱頭部正常形態,不正常的形式。

你不要讓所有的參數嚴格,因爲有些功能必須是非嚴格一些他們的論據在所有的工作,有些是如果不太嚴格高效的所有參數。

對於examplepartition必須是非嚴格在其第二個參數上無限列表在所有的工作,更一般在foldr使用必須是非嚴格的第二個參數中的每個函數,在無限列表工作。上有限列表,具有該功能的非嚴格的第二個參數可以使它顯着更有效(foldr (&&) True (False:replicate (10^9) True))。

你讓爭吵嚴格,如果你知道,在任何有價值的工作可以做反正參數必須進行評估。在許多情況下,GHC的嚴格性分析器可以自行完成,但當然不是。

一個非常典型的情況是在循環中或尾部遞歸,其中添加嚴格防止在途中巨大的thunk建設蓄電池。

我不知道哪裏有嚴格的規定,對我來說這是一個經驗問題,過了一段時間,你會學到在哪些地方增加嚴格度可能會有幫助以及傷害的地方。

作爲一個經驗法則,這是有道理的,以保持較小的數據(如Int)評價,但也有例外。

如何查看並檢查我的程序中是否有空間泄漏?什麼是構成空間泄漏的一般模式?

第一步是使用+RTS -s選項(如果程序與啓用了rtsopts的鏈接)。這表明你整體使用了多少內存,並且你可以經常判斷你是否有泄漏。 (另外,該程序需要與啓用rtsopts被鏈接) 更翔實的輸出可從與+RTS -hT選項,產生一個堆輪廓,可以幫助定位的空間泄漏運行的程序來獲得。

如果需要進一步的分析,該程序需要啓用分析編譯(-rtsops -prof -fprof-auto,中老年GHCs的-fprof-auto選項不可用,則-prof-auto-all選項有最接近的對應)。

然後你運行它與各種分析選項,並查看生成的堆配置文件。

空間泄漏的兩個最常見的原因是

  • 太懶惰
  • 太嚴格

第三位可能是採取不必要的共享,GHC做一點公共子表達式消除,但偶爾會在不需要的地方共享長列表。

爲了找到泄漏的原因,我再次知道沒有硬性規定,有時可以通過在一個地方添加嚴格性或在另一個地方添加懶惰來修復泄漏。

我該如何看看是否有太多懶惰的問題?我總是可以檢查堆分析,但我想知道什麼是懶惰傷害的一般原因,示例和模式?

一般來說,懶惰而被通緝,其中結果可以逐步建立起來的,和不想要在沒有結果的一部分可以前處理完成,像留下摺痕或一般尾遞歸函數傳遞。

+0

感謝一個非常好的答案。 – Satvik

3

我推薦閱讀關於PragmasRewrite Rules的GHC文檔,因爲它們解決了許多關於SPECIALIZE和RULES的問題。

簡單談談您的問題:

  • 專門用於強制編譯器來構建一個多態函數的專用版本爲特定類型。優點是在這種情況下應用該函數將不再需要字典。缺點是它會增加程序的大小。對於在「內部循環」中調用的函數來說,專門化特別有價值,對於不經常調用的頂級函數來說,它本質上是無用的。與INLINE交互請參考GHC documentation

  • RULES允許您指定重寫規則,您知道這些規則是有效的,但編譯器無法自行推斷。常見的例子是{-# RULES "mapfusion" forall f g xs. map f (map g xs) = map (f.g) xs #-},它告訴GHC如何融合map。由於干擾INLINE,可能會讓GHC使用規則。 7.19.3涉及如何避免衝突,以及如何強制GHC使用規則,即使它通常會避免衝突。

  • 在尾遞歸函數中,嚴格參數對像累加器之類的東西至關重要。你知道價值最終將被完全計算,並且建立一堆關閉來延遲計算完全失敗了目的。強制嚴格必須自然地避免,只要該函數可以應用於必須被懶惰地處理的值,如無限列表。一般來說,最好的想法是最初只強制顯示有用的地方(如累加器),然後僅在分析顯示需要時才添加更多。

  • 我的經驗是,大多數顯示停止的空間泄漏來自懶惰的累加器和非常大的數據結構中的未評估的懶惰值,儘管我確定這是特定於您正在編寫的程序類型。儘可能使用拆箱數據結構可以解決很多問題。

  • 實例之外那裏懶惰導致空間泄漏,它應避免的主要情況是IO。懶惰地處理資源固有增加所需要的資源的的掛鐘時間量。這可不好緩存性能,並且如果別的東西要專有權使用相同的資源這顯然是不好的。