GHCにおける自動専門化の推移性


392

GHC 7.6 のドキュメントから:

そもそも、SPECIALIZEプラグマは必要ないことがよくあります。モジュールMをコンパイルすると、GHCのオプティマイザー(-O付き)は、Mで宣言された各トップレベルのオーバーロード関数を自動的に考慮し、Mで呼び出されるさまざまな型に特化します。オプティマイザーは、インポートされた各INLINABLEオーバーロード関数も考慮します。 Mで呼び出されるさまざまな型に特化しています。

そして

さらに、関数fにSPECIALIZEプラグマが指定されると、GHCは、それらがSPECIALIZEプラグマと同じモジュールにある場合、またはINLINABLEである場合、fによって呼び出されるすべての型クラスオーバーロード関数の特殊化を自動的に作成します。など、推移的に。

したがって、GHCはプラグマなしでマークされた一部/ほとんど/すべての(?)関数を自動的に特殊化する必要があります明示的なプラグマを使用すると、特殊化は推移的です。私の質問は、自動専門化は推移的ですか?INLINABLE

具体的には、ここに小さな例があります:

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:

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はへの呼び出しを特殊化しますが、パフォーマンスを低下させるインスタンスを特殊化しplusませ(+)Qux Num

ただし、明示的なプラグマ

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

ドキュメントが示すように推移的な特殊化(+)が発生するため、特殊化され、コードは30倍速くなります(両方ともでコンパイルされます-O2)。これは予想される動作ですか?(+)明示的なプラグマで推移的に専門化することだけを期待すべきですか?


更新

7.8.2のドキュメントは変更されておらず、動作も同じであるため、この質問は依然として関連しています。


33
私は答えを知らないが、それはそれが関係している可能性があるように聞こえる:ghc.haskell.org/trac/ghc/ticket/5928おそらく価値が開く新しいチケットまたはあなたはそれがおそらく5928に関連すると思う場合はそこにあなたの情報を追加します
jberryman

6
同等のは、1)チケットに:@jberrymanそのチケットと私の質問2つの違いがあるように思えplusしませ simonpjは、いくつかは、チケットコード付きに行くがインライン化されることを示したが、よりコア)INLINABLE及び2としてマーク私の例は、どの関数もインライン化されなかったことを示しています(特に、2番目のFooコンストラクター、それ以外の場合はGHCインラ​​イン化されたもの)を取り除くことができませんでした。
crockeea 14

5
うん、いいよ。plus (Bar v1) = \(Bar v2)-> Bar $ v1 + v2LHSが呼び出しサイトで完全に適用されるようにを定義するとどうなりますか?インライン化された後、専門分野が導入されますか?
jberryman 2014

3
@jberrymanおかしなことを聞​​いてください。私はこのトラクレポートにつながったこの質問でその道を進んでいます。私はもともとそれらのリンクのために完全に適用された呼び出しを最初に持っていましたが、実際にはあまり専門性がありませんでした:への呼び出しも専門化されていませんでした。私にはそれについての説明はありませんが、別の質問のために残しておくつもりでした、またはこの質問に対する回答でそれが解決されることを願っています。plusplus
crockeea 14

11
ghc.haskell.org/trac/ghc/wiki/ReportABugから:「疑わしい場合は、バグを報告してください。」特にここで経験豊富なハスケラーの十分な数があなたの質問に答える方法を知らないので、あなたは気分を悪くすべきではありません。このようなテストケースは、GHC開発者にとっておそらく本当に価値があります。とにかく頑張ってね!チケットを提出した場合の質問を更新
jberryman '12 / 02/14

回答:


4

短い答え:

質問のポイントは、私が理解しているように、次のとおりです。

  • 「自動専門化は推移的ですか?」
  • (+)が明示的なプラグマで推移的に専門化されることだけを期待するべきですか?
  • (どうやら意図された)これはGHCのバグですか?それはドキュメントと矛盾していますか?

私の知る限り、答えは「いいえ」であり、ほとんどが「はい」ですが、他の手段があります。

コードのインライン化とタイプアプリケーションの特殊化は、速度(実行時間)とコードサイズの間のトレードオフです。デフォルトのレベルでは、コードを肥大化することなく、ある程度のスピードアップが得られます。より徹底的なレベルの選択は、SPECIALISEプラグマを介してプログラマーの裁量に任され ます。

説明:

オプティマイザは、インポートされた各INLINABLEオーバーロード関数も考慮し、Mで呼び出されるさまざまなタイプに特化します。

仮定するfと、その型にはa、型クラスによって制約された型変数が含まれますC a。デフォルトでGHCは、特化f型アプリケーション(置換に関してaためのt場合)f、同じモジュール内の()任意の関数、または(b)のソースコードにおけるそのタイプのアプリケーションで呼び出される場合にfマークされているINLINABLE他のモジュールすなわち次いで、 輸入 fからB。したがって、自動特殊化は推移的ではなく、INLINABLEインポートされ、のソースコードで呼び出され関数にのみ触れますA

あなたの例ではNum、次のようにインスタンスを書き換えた場合:

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

quxAdd (Qux x) (Qux y) = Qux $ U.zipWith (+) x y
  • quxAddは特にによってインポートされませんMainMainのインスタンスディクショナリをインポートします。Num (Qux Int)このディクショナリにはquxAdd、のレコードが含まれ(+)ます。ただし、辞書はインポートされますが、辞書で使用されているコンテンツはインポートされません。
  • plusは呼び出さず、のインスタンスディクショナリのレコードにquxAdd保存されている関数を使用します。このディクショナリは、コンパイラによって呼び出しサイト()で設定されます。(+)Num tMain
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.