2014-01-12 8 views
154

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

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

関心のコードはFoo.hsplusFastCycあり、ここで再現:

{-# 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つのドライバがあります:〜83秒で走る〜3秒で実行vtTest、およびfcTestを、 forallのd特殊化を使用して-O3でコンパイルされます。

vtTest試験のため、付加コードがUnboxedベクトルに特化されているcore showsIntオーバーSは、等、一般的なベクトルコードがfcTestのために使用されます。 オンラインでは、GHCがplusFastCycの特殊バージョンを167行目の汎用バージョンと比較して書いていることがわかります。 特殊化のルールは225行目です。このルールは270行目で発生するはずです。(main6iterate main8 yを呼び出し、そのplusFastCycを専門とする場所main8です。)

私の目標は、plusFastCycを専門とすることにより、早くvtTestとしてfcTestを作ることです。 fcTestGHC.Extsから

  1. 明示的に呼び出しinline:私はこれを行うには、2つの方法を見つけました。
  2. plusFastCycFactored m Intの制約を削除します。

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

私は、INLINEINLINABLE、およびSPECIALIZEを使用してさまざまなオプションを試しましたが、何も動作しないようです。 (EDIT:例を小さくするためにplusFastCycを取り除いた可能性がありますので、INLINEは関数をインライン化する可能性があります。plusFastCycが大きすぎるため、これは実際のコードでは起こりません。)この特定の例では、例を最小限にする前に多くのmatch_co警告を受け取っていましたが、私はmatch_co: needs more casesまたはRULE: LHS too complicated to desugar(およびhere)の警告を受け取りません。恐らく、 "問題"はルール内の制約であるFactored m Intです。その制約に変更を加えると、fcTestvtTestという速さで実行されます。

私は何かGHCをしていますか?なぜGHCはplusFastCycを専門としないのですか?それをどうすれば作れますか?

UPDATE

問題はGHC 7.8.2で解消されないので、この質問はまだ関連しています。

+3

私は、特定の* m、すなわちMを専門に試しました。これは仕事を終わらせましたが、実際のプログラムで特定のファントムの種類を具体化することはできません。 – crockeea

+0

私はまた、GHCバグレポートhttps://ghc.haskell.org/trac/ghc/ticket/8668を提出しましたが、問題はまだ開いています。バグ報告プロセスは私が少し質問をきれいにするのを助けました、うまくいけば、何が起こっているのか把握するのがより簡単になります。 – crockeea

+0

@monojohnnyごめんなさい、私はあなたがそのようにフラグを立てることができると信じています。私はGHCにかなり妥当なことをするよう求めていると思います。それはできません。私はそれが間違っているかどうか、またはこれが回避策を持っている可能性のあるコンパイラの特質であるかどうかはわかりません。私は現時点で私を逃しているハッカーに関する特定のライブラリの専門化とルールの回避策を見てきたので、私自身よりもGHCの経験が豊富なコミュニティの誰かが専門化の仕方を知っていることを願っています。 – crockeea

答えて

4

また、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 

この変更を、しかし、希望のスピードアップを実現しませんでした。

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

これはLANGUAGEOverlappingInstancesFlexibleInstancesを追加する必要がありますどのようなもの性能向上を達成したことは、次のように、同じ関数定義を持つタイプVT U.Vector m Intに特化したインスタンスを追加し、手動でました。

興味深いことに、この例のプログラムでは、SPECIALIZEおよびINLINABLEプラグマをすべて削除しても、重複するインスタンスで得られるスピードアップが残っています。

+0

確かに最適ではありませんが、実際に目標を達成する最初の解決策ですので、今のところそれを取ると思います... – crockeea

関連する問題