2012-04-22 9 views
8

で部分的に計算を最適化:私はこのコードを最適化する方法を興味ハスケル

その ff0gg0、および hは全て高価であるが、 lの作成と保存が非常にあると仮定すると
fun n = (sum l, f $ f0 l, g $ g0 l) 
    where l = map h [1..n] 

高価な。

返されたタプルが完全に評価されるかガベージコレクションされるまで、lが格納されます。代わりに、length l,f0 l、およびg0 lは、いずれか1つを実行するときにすべて実行する必要がありますが、fおよびgを遅延させる必要があります。

fun n = a `seq` b `seq` c `seq` (a, f b, g c) 
    where 
    l = map h [1..n] 
    a = sum l 
    b = inline f0 $ l 
    c = inline g0 $ l 

または非常に類似した:それは、この動作は、書面によって修正される可能性が表示されます

fun n = (a,b,c) `deepSeq` (a, f b, g c) 
    where ... 

我々は、おそらく同様に同様の効果を得るために、内部のタイプの束を指定することができ、痛いようです。他のオプションはありますか?また

、私は明らかにコンパイラが作成し、用語によってl用語を消費し、単一のループにsumf0、およびg0を融合することを私inline Sと願っています。手動のインライン展開でこれを明示的にすることもできますが、それはうまくいくでしょう。リストlが明示的に作成されないようにする方法やインライン展開を強制する方法はありますか?おそらく、コンパイル時にインライン展開やフュージョンが失敗した場合に警告やエラーを生成するプラグマ?余談として

は、私はすべてのプレリュードでlet x = x in xによって定義されている理由seqinlinelazyなどについて興味があります。これは単純にコンパイラがオーバーライドするための定義を与えることですか?

+4

最後の質問への回答:http://stackoverflow.com/a/8654407/1011995 –

+0

'f0'と' g0'は完全に任意であるか、 'foldr'の形で書くことができますか? – dave4420

+1

(a、b、c) - アキュムレータで簡単に折り畳むことはできませんか? – Sarah

答えて

3

あなたが確信したいのであれば、唯一の方法はあなた自身で行うことです。任意のコンパイラバージョンについて、いくつかのソース形式を試して、生成されたコア/アセンブリ/ llvmバイトコード/それがあなたが望むものを実行するかどうかを調べることができます。しかし、それは新しいコンパイラバージョンごとに壊れる可能性があります。

あなたは

fun n = a `seq` b `seq` c `seq` (a, f b, g c) 
    where 
    l = map h [1..n] 
    a = sum l 
    b = inline f0 $ l 
    c = inline g0 $ l 

またはそのdeepseqバージョンを記述する場合、コンパイラは単一の間に(ない並行処理の意味で)並列に実行されるようにabcの計算をマージすることができるかもしれませんlのトラバーサルですが、当面はGHCはそうではないと確信しています.JHCやUHCの場合は驚いています。そして、計算の構造bcは十分に単純である必要があります。

コンパイラとコンパイラのバージョン間で移植可能な結果を​​得る唯一の方法は、自分で行うことです。今後数年間、少なくとも。

f0g0によっては、有名な平均

data P = P {-# UNPACK #-} !Int {-# UNPACK #-} !Double 

average :: [Double] -> Double 
average = ratio . foldl' count (P 0 0) 
    where 
    ratio (P n s) = s/fromIntegral n 
    count (P n s) x = P (n+1) (s+x) 

しかし、もしf0及び/又はg0の構造のように、適切な蓄圧式に厳密左倍を行う機能を組み合わせるのと同じくらい簡単であるかもしれません左右の折り畳みが合わない場合、左の折りたたみともう一つの折り畳みのように、1つの横断で計算を行うことは不可能かもしれません。そのような場合は、lを再作成し、lを保存するかのいずれかを選択します。 lを格納することは、明示的な共有(where l = map h [1..n])で実現するのは簡単ですが、コンパイラで共通の部分式を削除すると達成するのが難しい場合があります(残念ながらGHCではCSE )。 GHCの場合、フラグfno-cse-fno-full-lazinessは、望ましくない共有を避けるのに役立ちます。

+0

ああ、左足と右足の面白い点!私はあなたのCSEのポイントについて少し混乱しています。あなたは、単純にCSEをコード化しようとすると、この問題が発生することを単に観察していますか? –

+0

リストを保存するよりもリストを再作成するほうが安い場合は、たとえば次のように記述します。 'f0(マップh [1 .. n])'と 'g0(マップh [1 .. n])'のようになります。しかし、コンパイラは、共通部分式 'map h [1. .. n]'を削除し、計算の間に共有することができます。それが望ましくない場合、それを防ぐことは、部分表現を共有すること(それを名前にバインドすると実行されます。 'l = map h [1. ..]])です。基本的にはい、CSEはその問題を導入することができますし、回避するのは難しいかもしれません。 –

関連する問題