2013-06-04 19 views
13

同じモジュール内でCriterionと測定すると、よく実行されると思われる最長共通部分シーケンスを計算する非再帰関数があります(ghc 7.6.1-O2 -fllvmフラグでコンパイルされます)。私は(推奨hereなど)モジュール、輸出がちょうどその関数に関数を変換し、基準を再び測定した場合、私は戻ってモジュールに基準テストを移動した場合一方、私が消える〜2倍の減速を(取得します関数が定義されている場所)。私はINLINEプラグマで関数をマークしてみましたが、これはモジュール間の性能測定に何の違いもありませんでした。GHCのモジュール間最適化

GHCは、関数とメイン(関数が到達可能)が同じモジュール内にあるときにうまく機能する厳密性分析を行っている可能性があります。関数をモジュール化して、他のモジュールから呼び出されたときにうまく機能するようにする方法についての指摘を感謝します。問題のコードは大きすぎてここに貼り付けることはできません。試してみたい場合はhereをご覧ください。私がやろうとしています何の小さな例は、以下の(コードのスニペットを持つ)である:

-- Function to find longest common subsequence given unboxed vectors a and b 
-- It returns indices of LCS in a and b 
lcs :: (U.Unbox a, Eq a) => Vector a -> Vector a -> (Vector Int,Vector Int) 
lcs a b | (U.length a > U.length b) = lcsh b a True 
     | otherwise = lcsh a b False 

-- This section below measures performance of lcs function - if I move it to 
-- a different module, performance degrades ~2x - mean goes from ~1.25us to ~2.4us 
-- on my test machine 
{-- 
config :: Config 
config = defaultConfig { cfgSamples = ljust 100 } 

a = U.fromList ['a'..'j'] :: Vector Char 
b = U.fromList ['a'..'k'] :: Vector Char 

suite :: [Benchmark] 
suite = [ 
      bench "lcs 10" $ whnf (lcs a) b 
     ] 

main :: IO() 
main = defaultMainWith config (return()) suite 
--} 
+0

代わりにINLINEABLEを試してみてください。それはうまくいくかもしれません。 – Carl

+0

@Carlは、lcs機能を試しました。まだ同じ。 – Sal

+5

GHCはすべてが1つのモジュールに入っているときに、型変数 'a'を' Char'に特化することができるという問題があると考えています。あなたは 'SPECIALIZE'プラグマを使って遊んでみることができます(または単に' Char'に手動で変更してください)。 – hammar

答えて

13

hammaris right、重要な問題は、コンパイラがlcsが同時にで使用されていることのタイプを見ることができるということですそれはコードを見ることができるので、コードをその特定のタイプに特化することができます。

コードが使用される型がコンパイラによって認識されない場合、コンパイラは多態性コードだけを生成することはできません。そしてそれはパフォーマンスにとって悪いです - 私はむしろ、〜2倍の差しかないことに驚いています。多型コードは、多くの操作のために型クラスのルックアップが必要であることを意味し、それは、少なくとも、それが不可能ルックアップ関数または定数倍のサイズをインライン化することができる[例えばボックス化されていない配列/ベクトルアクセスの場合]。

1モジュールのケースと同等の性能を実装で使用することはできず、別個のモジュールで使用することはできません(使用サイトで特殊化が必要なコードを表示する必要はありません、{-# SPECIALISE foo :: Char -> Int, foo :: Bool -> Integer #-}など)。

使用サイトでコードを表示するには、通常、インタフェースファイルに展開するコードを{-# INLINABLE #-}というマークを付けて公開します。

クロスモジュールパフォーマンス測定で差異を生じなかったINLINEプラグマで機能をマークしようとしました。唯一

lcs :: (U.Unbox a, Eq a) => Vector a -> Vector a -> (Vector Int,Vector Int) 
lcs a b | (U.length a > U.length b) = lcsh b a True 
     | otherwise = lcsh a b False 

INLINEまたはINLINABLEをマーキング

がコースの違いはありません、その関数は自明であり、それはとても小さいですので、コンパイラは、そのとにかく展開公開します。アンフォールディングが公開されていない場合でも、その差は測定できません。

あなたは(cmpがここで非常に重要だものですが、他は1に必要であり、多型のものの少なくともその、あまりにも実際の仕事をしている機能のunfoldingsを公開するlcshfindSnakesgridWalkcmp必要。cmpが必要であることを確認してください。2.専門家cmpを呼び出してください。

ものINLINABLE作り、別モジュールケース

$ ./diffBench 
warming up 
estimating clock resolution... 
mean is 1.573571 us (320001 iterations) 
found 2846 outliers among 319999 samples (0.9%) 
    2182 (0.7%) high severe 
estimating cost of a clock call... 
mean is 40.54233 ns (12 iterations) 

benchmarking lcs 10 
mean: 1.628523 us, lb 1.618721 us, ub 1.638985 us, ci 0.950 
std dev: 51.75533 ns, lb 47.04237 ns, ub 58.45611 ns, ci 0.950 
variance introduced by outliers: 26.787% 
variance is moderately inflated by outliers 

と単一モジュールケース

$ ./oneModule 
warming up 
estimating clock resolution... 
mean is 1.726459 us (320001 iterations) 
found 2092 outliers among 319999 samples (0.7%) 
    1608 (0.5%) high severe 
estimating cost of a clock call... 
mean is 39.98567 ns (14 iterations) 

benchmarking lcs 10 
mean: 1.523183 us, lb 1.514157 us, ub 1.533071 us, ci 0.950 
std dev: 48.48541 ns, lb 44.43230 ns, ub 55.04251 ns, ci 0.950 
variance introduced by outliers: 26.791% 
variance is moderately inflated by outliers 

の差がbearably小さいです。

+0

良い点。私はこれを分析しようとすると専門性を忘れてしまった。 – Sal

関連する問題