GHC中自動專業化的傳遞性

本文翻譯自:Transitivity of Auto-Specialization in GHC

From the docs for GHC 7.6: 來自GHC 7.6 的文檔

[Y]ou often don't even need the SPECIALIZE pragma in the first place. [Y]你通常甚至不需要SPECIALIZE pragma。 When compiling a module M, GHC's optimiser (with -O) automatically considers each top-level overloaded function declared in M, and specialises it for the different types at which it is called in M. The optimiser also considers each imported INLINABLE overloaded function, and specialises it for the different types at which it is called in M. 在編譯模塊M時,GHC的優化器(帶-O)自動考慮在M中聲明的每個頂級重載函數,並將其專門用於在M中調用它的不同類型。優化器還考慮每個導入的INLINABLE重載函數,並將其專門用於M中調用的不同類型。

and

Moreover, given a SPECIALIZE pragma for a function f, GHC will automatically create specialisations for any type-class-overloaded functions called by f, if they are in the same module as the SPECIALIZE pragma, or if they are INLINABLE; 此外,給定函數f的SPECIALIZE編譯指示,GHC將自動爲f調用的任何類型類重載函數創建特殊化,如果它們與SPECIALIZE編譯指示位於同一模塊中,或者它們是否爲INLINABLE; and so on, transitively. 等等,過渡性的。

So GHC should automatically specialize some/most/all(?) functions marked INLINABLE without a pragma, and if I use an explicit pragma, the specialization is transitive. 因此,GHC應該自動專門化一些/大多數/所有(?)函數,標記爲INLINABLE 而不使用編譯指示,如果我使用顯式編譯指示,則特化是可傳遞的。 My question is: is the auto -specialization transitive? 我的問題是: auto -specialization是否具有傳遞性?

Specifically, here's a small example: 具體來說,這是一個小例子:

Main.hs: Main.hs:

import Data.Vector.Unboxed as U
import Foo

main =
    let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
        (Bar (Qux ans)) = iterate (plus y) y !! 100
    in putStr $ show $ foldl1' (*) ans

Foo.hs: Foo.hs:

module Foo (Qux(..), Foo(..), plus) where

import Data.Vector.Unboxed as U

newtype Qux r = Qux (Vector r)
-- GHC inlines `plus` if I remove the bangs or the Baz constructor
data Foo t = Bar !t
           | Baz !t

instance (Num r, Unbox r) => Num (Qux r) where
    {-# INLINABLE (+) #-}
    (Qux x) + (Qux y) = Qux $ U.zipWith (+) x y

{-# INLINABLE plus #-}
plus :: (Num t) => (Foo t) -> (Foo t) -> (Foo t)
plus (Bar v1) (Bar v2) = Bar $ v1 + v2

GHC specializes the call to plus , but does not specialize (+) in the Qux Num instance which kills performance. GHC專門調用plus ,但擅長(+)Qux Num殺死性能實例。

However, an explicit pragma 但是,一個明確的pragma

{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}

results in transitive specialization as the docs indicate, so (+) is specialized and the code is 30x faster (both compiled with -O2 ). 如文檔所示,導致傳遞特化 ,因此(+)是專用的,代碼快30倍(都用-O2編譯)。 Is this expected behavior? 這是預期的行爲嗎? Should I only expect (+) to be specialized transitively with an explicit pragma? 我是否應該只期望(+)具有明確的編譯指示的傳遞?


UPDATE UPDATE

The docs for 7.8.2 haven't changed, and the behavior is the same, so this question is still relevant. 7.8.2的文檔沒有改變,行爲是相同的,所以這個問題仍然是相關的。


#1樓

參考:https://stackoom.com/question/1SDjr/GHC中自動專業化的傳遞性


#2樓

Short answers: 簡短的答案:

The question's key points, as I understand them, are the following: 正如我所理解的那樣,問題的關鍵點如下:

  • "is the auto-specialization transitive?" “是自動專業傳遞嗎?”
  • Should I only expect (+) to be specialized transitively with an explicit pragma? 我是否應該只期望(+)具有明確的編譯指示的傳遞?
  • (apparently intended) Is this a bug of GHC? (顯然是有意的)這是GHC的錯誤嗎? Is it inconsistent with the documentation? 它與文檔不一致嗎?

AFAIK, the answers are No, mostly yes but there are other means, and No. AFAIK,答案是否定的,大部分都是,但還有其他方法,而且不是。

Code inlining and type application specialization is a trade-off between speed (execution time) and code size. 代碼內聯和類型應用程序專門化是速度(執行時間)和代碼大小之間的權衡。 The default level gets some speedup without bloating the code. 默認級別獲得一些加速,而不會膨脹代碼。 Choosing a more exhaustive level is left to the programmer's discretion via SPECIALISE pragma. 選擇更詳盡的級別由程序員通過SPECIALISE pragma自行決定。

Explanation: 說明:

The optimiser also considers each imported INLINABLE overloaded function, and specialises it for the different types at which it is called in M. 優化器還會考慮每個導入的INLINABLE重載函數,並將其專門用於M中調用它的不同類型。

Suppose f is a function whose type includes a type variable a constrained by a type class C a . 假設f是一個函數,其類型包括類型變量a由類型約束類C a GHC by default specializes f with respect to a type application (substituting a for t ) if f is called with that type application in the source code of (a) any function in the same module, or (b) if f is marked INLINABLE , then any other module that imports f from B . GHC默認專門f相對於應用程序的類型(代替a用於t )如果f被調用,在同一模塊中的(a)的任何函數的源代碼的類型的應用程序,或(b)如果f被標記INLINABLE ,然後從B 導入 f任何其他模塊。 Thus, auto-specialization is not transitive, it only touches INLINABLE functions imported and called for in the source code of A . 因此,自動特化不是傳遞性的,它只接觸 A 的源代碼中導入和調用的INLINABLE函數。

In your example, if you rewrite the instance of Num as follows: 在您的示例中,如果您重寫Num的實例,如下所示:

instance (Num r, Unbox r) => Num (Qux r) where
    (+) = quxAdd

quxAdd (Qux x) (Qux y) = Qux $ U.zipWith (+) x y
  • quxAdd is not specifically imported by Main . quxAdd不是由Main專門導入的。 Main imports the instance dictionary of Num (Qux Int) , and this dictionary contains quxAdd in the record for (+) . Main導入Num (Qux Int)的實例字典,該字典在(+)的記錄中包含quxAdd However, although the dictionary is imported, the contents used in the dictionary are not. 但是,雖然導入了字典,但字典中使用的內容卻不是。
  • plus does not call quxAdd , it uses the function stored for the (+) record in the instance dictionary of Num t . plus不調用quxAdd ,它使用爲Num t的實例字典中的(+)記錄存儲的函數。 This dictionary is set at the call site (in Main ) by the compiler. 該字典由編譯器在調用站點(在Main )設置。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章