2011-01-28 10 views
12

私はData.Binary.Putモナドを別のものにラップしようとしていますので、後で "書き込むバイト数"や "ファイルの現在の位置"などの質問をすることができます。しかし、のようにも非常に些細なラップ:なぜData.Binary.Putモナドをラップするとメモリリークが発生するのですか?

data Writer1M a = Writer1M { write :: P.PutM a } 
or 
data Writer2M a = Writer2M { write :: (a, P.Put) } 

は、巨大な宇宙漏れやプログラムを作成し、通常は(4GBのRAMを取った後)がクラッシュします。

-- This works well and consumes almost no memory. 

type Writer = P.Put 

writer :: P.Put -> Writer 
writer put = put 

writeToFile :: String -> Writer -> IO() 
writeToFile path writer = BL.writeFile path (P.runPut writer) 

-- This one will cause memory leak. 

data Writer1M a = Writer1M { write :: P.PutM a } 

instance Monad Writer1M where 
    return a = Writer1M $ return a 
    ma >>= f = Writer1M $ (write ma) >>= \a -> write $ f a 

type WriterM = Writer1M 
type Writer = WriterM() 

writer :: P.Put -> Writer 
writer put = Writer1M $ put 

writeToFile :: String -> Writer -> IO() 
writeToFile path writer = BL.writeFile path (P.runPut $ write writer) 
-- This one will crash as well with exactly the 
-- same memory foot print as Writer1M 

data Writer2M a = Writer2M { write :: (a, P.Put) } 

instance Monad Writer2M where 
    return a = Writer2M $ (a, return()) 
    ma >>= f = Writer2M $ (b, p >> p') 
         where (a,p) = write ma 
           (b,p') = write $ f a 

type WriterM = Writer2M 
type Writer = WriterM() 

writer :: P.Put -> Writer 
writer put = Writer2M $ ((), put) 

writeToFile :: String -> Writer -> IO() 
writeToFile path writer = BL.writeFile path (P.runPut $ snd $ write writer) 

を私はHaskellのに新たなんだ、これは私に何のローミングサービスを行うものではありませんが、ラッパーモナドは非常に些細なように見えるので、私はそこに推測している:ここで私はこれまで試したものです私は行方不明です。

ありがとうございます。

UPDATE:http://hpaste.org/43400/why_wrapping_the_databinaryp

アップデート2: この質問hereへの第二の部分もあり ここでは、問題を示すサンプルコードです。

+1

どのコンパイラフラグを使用していますか? –

+0

あなたが質問したところで、私は-O2で試しましたが(私は以前は使っていませんでしたが)メモリのフットプリントは変わりませんでした。 –

+0

簡単なテストプログラムを投稿して、他の人が自分でビルドする必要はありませんか? –

答えて

4

ビットのためにチャンスをうかがった後、私は(問題が実装するバイナリの(>> =)の使用のようだことがわかりました>>)。

m >> k = Writer1M $ write m >>= const (write k) 

は(>>)、binary's sourceを見ると最初のモナドの結果を破棄するようだ:このバージョンはまだメモリがリークするのに対し

m >> k = Writer1M $ write m >> write k 

:Writer1Mモナドの実装に以下の追加は、問題を解決します明示的に。しかし、これがいかに正確に漏れを防止するかはわかりません。私の最高の理論は、そうでなければGHCがPairSオブジェクト上に保持され、 "a"参照は決して見られないので漏れます。

2

モナドをさらに作成しようとしました?例えば。あなたのデータ型のコンストラクタをstrict/newtypeで置き換えるようにしてください。

私はここで何が問題なのかよくわかりませんが、これは通常のリークの原因です。

PS:そして、例えば、不要なラムダを削除しよう:

ma >>= f = Writer1M $ (write ma) >=> write . f 
+0

データをnewtypeに変更することは#haskellの良い人も同様に示唆していることであり、不幸にもそれを変更してラムダを削除することは、メモリフットプリントを変更しなかったことを示唆しています。しかし、提案に感謝します。 –

+0

プロファイリングも試しましたか? – fuz

+0

はい、結果は次のとおりです:http://i.imgur.com/4Q2E3.png、ラッパーのいずれかを使用すると、黄色の領域が表示されます。 –