У меня есть conduit конвейер, обрабатывающий длинный файл. Я хочу распечатать отчет о проделанной работе для каждого 1000 записей, поэтому я написал следующее:
-- | Every n records, perform the IO action.
-- Used for progress reports to the user.
progress :: (MonadIO m) => Int -> (Int -> i -> IO ()) -> Conduit i m i
progress n act = skipN n 1
where
skipN c t = do
mv <- await
case mv of
Nothing -> return ()
Just v ->
if c <= 1
then do
liftIO $ act t v
yield v
skipN n (succ t)
else do
yield v
skipN (pred c) (succ t)
Независимо от того, с каким действием я это называю, он утечки памяти, даже если я просто скажу, чтобы он распечатал полную остановку.
Насколько я вижу, функция является хвостовой рекурсивной, и оба счетчика регулярно принудительно (я попытался поставить "seq c" и "seq t", безрезультатно). Любая подсказка?
Если я добавлю "awaitForever", который печатает отчет для каждой записи, тогда он отлично работает.
Обновление 1: Это происходит только при компиляции с -O2. Профилирование указывает, что утечка памяти выделяется в рекурсивной функции "skipN" и сохраняется "SYSTEM" (что бы это ни значило).
Обновление 2: мне удалось вылечить его, по крайней мере, в контексте моей текущей программы. Я заменил эту функцию выше. Обратите внимание, что "proc" имеет тип "Int → Int → Maybe я → m()": для его использования вы вызываете "ожидание" и передаете ему результат. По какой-то причине переключение на "ожидание" и "выход" решило проблему. Итак, теперь он ждет следующего ввода, прежде чем уступить предыдущему результату.
-- | Every n records, perform the monadic action.
-- Used for progress reports to the user.
progress :: (MonadIO m) => Int -> (Int -> i -> IO ()) -> Conduit i m i
progress n act = await >>= proc 1 n
where
proc c t = seq c $ seq t $ maybe (return ()) $ \v ->
if c <= 1
then {-# SCC "progress.then" #-} do
liftIO $ act t v
v1 <- await
yield v
proc n (succ t) v1
else {-# SCC "progress.else" #-} do
v1 <- await
yield v
proc (pred c) (succ t) v1
Итак, если у вас есть утечка памяти в кабелепроводе, попробуйте заменить выход и ждать действий.