2011-01-28 3 views
12

Я пытаюсь обернуть DataBinary.Put монаду в другую, чтобы позже я мог задать ему вопросы типа «сколько байтов он собирается писать» или «что такое текущая позиция в файле». Но даже очень тривиальные обручи нравится:Почему обертывание Data.Binary.Put монады создает утечку памяти?

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

создать огромную утечку пространства и программа, как правило, выходит из строя (после приема до 4 Гб оперативной памяти). Вот то, что я пытался до сих пор:

-- 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, и это не имеет никакого Sence мне, но обертка монада кажется очень тривиальной, так что я угадываю что-то очевидное, я пропал без вести.

Спасибо, что посмотрели.

UPDATE: Вот пример кода, который демонстрирует проблему: http://hpaste.org/43400/why_wrapping_the_databinaryp

UPDATE2: Существует также вторая часть этого вопроса here.

+1

Какие флаги компилятора вы используете? –

+0

После того, как вы спросили, что я попробовал с -O2 (раньше я не использовал его), но печать в памяти не изменилась. –

+0

Не могли бы вы опубликовать тривиальную тестовую программу, чтобы другие здесь не могли создавать свои собственные? –

ответ

4

После ковыряться немного, я обнаружил, что проблема, как представляется, использование бинарного-х (>> =) для реализации (>>). Следующее дополнение к реализации Writer1M монады решает эту проблему:

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

В то время как эта версия до сих пор утечки память:

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

Глядя на binary's source (>>), кажется, отказаться от результата первой монады в явном виде. Не уверен, как именно это предотвращает утечку. Моя лучшая теория заключается в том, что GHC в противном случае держится на объекте PairS, а ссылка «a» протекает, потому что ее никогда не рассматривают.

2

Вы пытались сделать монаду более strict? Например. попробуйте сделать конструкторы вашего типа данных строгими/замените их на новый тип.

Я не знаю, в чем тут проблема, но это обычный источник утечек.

PS: И попробуйте удалить ненужные лямбды, например:

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

Изменение данных в newtype - это то, что предложили хорошие люди в #haskell, к сожалению, это изменение и удаление лямбды, как вы предложили, не изменили печать стопы памяти. Но спасибо за предложение. –

+0

Вы тоже пробовали профилировать? – fuz

+0

Да, вот результат: http://i.imgur.com/4Q2E3.png, желтая область появляется, когда я использую одну из оберток. –

Смежные вопросы