Почему обертка монады Data.Binary.Put создает утечку памяти? - PullRequest
12 голосов
/ 28 января 2011

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

Спасибо за поиск.

ОБНОВЛЕНИЕ: Вот пример кода, который демонстрирует проблему: http://hpaste.org/43400/why_wrapping_the_databinaryp

ОБНОВЛЕНИЕ2: Тамтакже вторая часть этого вопроса здесь .

Ответы [ 2 ]

4 голосов
/ 30 января 2011

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

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

В то время как эта версия все еще теряет память:

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

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

2 голосов
/ 28 января 2011

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

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

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

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