制約付きの専門化


156

GHCにクラス制約のある関数を特殊化させるのに問題があります。私はここに私の問題の最低限の例がありますFoo.hsMain.hsを。2つのファイルがコンパイルされ(GHC 7.6.2、ghc -O3 Main)、実行されます。

注: Foo.hsは実際には削除されています。制約が必要な理由を知りたい場合は、ここでもう少しコードを見ることができます。コードを1つのファイルに配置するか、その他多くの小さな変更を加えた場合、GHCはへの呼び出しを単にインライン化しますplusFastCycplusFastCycマークされていても、GHCがインライン化するには大きすぎるため、これは実際のコードでは発生しませんINLINE。重要なのは、インライン化ではなく、への呼び出しを特化するplusFastCycことです。plusFastCycは実際のコードの多くの場所で呼び出されるため、GHCに強制することができたとしても、そのような大きな関数を複製することは望ましくありません。

対象のコードはplusFastCycFoo.hs、ここに複製されています。

{-# INLINEABLE plusFastCyc #-}
{-# SPECIALIZE plusFastCyc :: 
         forall m . (Factored m Int) => 
              (FastCyc (VT U.Vector m) Int) -> 
                   (FastCyc (VT U.Vector m) Int) -> 
                        (FastCyc (VT U.Vector m) Int) #-}

-- Although the next specialization makes `fcTest` fast,
-- it isn't useful to me in my real program because the phantom type M is reified
-- {-# SPECIALIZE plusFastCyc :: 
--          FastCyc (VT U.Vector M) Int -> 
--               FastCyc (VT U.Vector M) Int -> 
--                    FastCyc (VT U.Vector M) Int #-}

plusFastCyc :: (Num (t r)) => (FastCyc t r) -> (FastCyc t r) -> (FastCyc t r)
plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2

Main.hs:ファイルには、2つのドライバがあるvtTest、〜3秒で実行され、fcTest使用して-O3でコンパイルしたとき〜83秒で実行され、forall「D専門に。

コアショーのためのvtTest試験、付加コードがに特化されているUnboxedオーバーベクトルInt汎用ベクトル符号をするために使用されている間などの、fcTest。10行目では、あなたはGHCがの特殊なバージョンを記述していることがわかりますplusFastCyc、私は(このルールは、ライン270上の解雇すべきであると信じてライン167上のジェネリックバージョンに比べて専門のためのルールはライン225上で、main6通話iterate main8 yので、main8ありますどこplusFastCycに特化すべきか。)

私の目標は、専門にすることとfcTest同じくらい速く作ることvtTestですplusFastCyc。これを行う2つの方法を見つけました。

  1. 明示的に呼び出しinlineからGHC.Extsの中でfcTest
  2. Factored m Int制約を削除しますplusFastCyc

オプション1は、実際のコードベースでplusFastCycは頻繁に使用される操作と非常に大きな関数であるため、満足できません。したがって、使用するたびにインライン化する必要はありません。むしろ、GHCはの特殊なバージョンを呼び出す必要がありplusFastCycます。オプション2は、実際のコードに制約が必要なため、実際にはオプションではありません。

私が使用して(と使用していない)のさまざまなオプションを試してみたINLINEINLINABLESPECIALIZE、何も動いていないようにみえ。(編集:私があまりにも多くを取り除いてplusFastCyc私の例を小さくしたのでINLINE、関数がインライン化される可能性があります。これは実際のコードでplusFastCycは非常に大きいので起こりません。)この特定の例では、私はそうではありませんいずれかの取得match_co: needs more casesまたはRULE: LHS too complicated to desugar(とここで私は多くなっていたものの、)警告をmatch_co例を最小化する前に警告を。おそらく、「問題」はFactored m Intルールの制約です。その制約に変更を加えると、とfcTest同じくらい速く実行されvtTestます。

私はGHCが嫌いなことをしていますか?GHCはなぜを特化しないのplusFastCycですか、またどのようにして作成できますか?

更新

問題はGHC 7.8.2でも持続するので、この質問は依然として関連しています。


3
私は特定の m、つまりに特化してみましたM。これで仕事は完了しましたが、具体化されているため、実際のプログラムで特定のファントムタイプに特化することはできません。
crockeea 2014年

GHCバグレポートghc.haskell.org/trac/ghc/ticket/8668も提出しましたが、問題はまだ解決していません。バグレポートプロセスは、質問を少し整理するのに役立ちました。うまくいけば、何が起こっているのかを理解しやすくなるでしょう。
crockeea 2014年

@monojohnny申し訳ありませんが、そのようにフラグを立てることができます。私はGHCにかなり合理的な何かをするように頼んでいると思います、そしてそれはそれをしません。私がそれを間違っているのか、これが回避策を持っている可能性のあるコンパイラの特異な現象なのかはわかりません。私は今のところハッキングに関する特定のライブラリで特殊化とルールの回避策を見てきました。私は、コミュニティの誰かが自分より多くのGHC経験を持ち、特殊化を実現する方法を知っていることを望んでいます。
crockeea 2014年

1
コメントの調子をお詫び申し上げます。このサイトへの私の貢献ではありません-あなたの投稿には本当に何の問題もありません(私の理解の欠如が、私の悩みの原因だったと思います!)
monojohnny

@monojohnny謝罪は受け入れたが、それは;-)今でdownvoteがロックされていることを残念だ
crockeea

回答:


5

GHCは、SPECIALIZE型クラスのインスタンス宣言にもオプションを提供します。私はの(拡張された)コードでこれを試しFoo.hsて、以下を入れました:

instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where 
    {-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-}
    VT x + VT y = VT $ V.zipWith (+) x y

ただし、この変更では目的のスピードアップは実現されませんでした。そのパフォーマンスの向上を達成したのは、次のように、同じ関数定義を持つ型の特殊なインスタンスを手動で追加することですVT U.Vector m Int

instance (Factored m Int) => Num (VT U.Vector m Int) where 
    VT x + VT y = VT $ V.zipWith (+) x y

これには、OverlappingInstancesFlexibleInstancesを 追加する必要がありLANGUAGEます。

興味深いことに、プログラム例では、オーバーラップするインスタンスで得られたスピードアップは、あなたが、すべて削除しても残っているSPECIALIZEINLINABLEプラグマを。


間違いなく最適ではありませんが、それは実際に目標を達成する最初のソリューションなので、私は今のところそれを取ると思います...
crockeea
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.