gpt4 book ai didi

haskell - 为什么这会导致 Haskell Conduit 库中的内存泄漏?

转载 作者:行者123 更新时间:2023-12-04 02:02:07 27 4
gpt4 key购买 nike

我有一个 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)

无论我用什么 Action 调用它,它都会泄漏内存,即使我只是告诉它打印一个句号。

据我所见,该函数是尾递归的,并且两个计数器都被定期强制(我尝试将“seq c”和“seq t”放入,但无济于事)。有什么线索吗?

如果我放入为每条记录打印报告的“awaitForever”,那么它可以正常工作。

更新 1:这仅在使用 -O2 编译时发生。分析表明泄漏内存是在递归“skipN”函数中分配的,并由“SYSTEM”保留(无论这意味着什么)。

更新 2:我已经设法治愈它,至少在我当前程序的上下文中。我已经用这个替换了上面的函数。请注意,“proc”的类型为“Int -> Int -> Maybe i -> m ()”:要使用它,您调用“await”并将结果传递给它。出于某种原因,交换“等待”和“产量”解决了这个问题。所以现在它在产生上一个结果之前等待下一个输入。
-- | 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

因此,如果您在 Conduit 中存在内存泄漏,请尝试交换 yield 和 await 操作。

最佳答案

这不是 anwser,但它是我为测试而编写的一些完整代码。我根本不知道管道,所以它可能不是最好的管道代码。我已经强制了所有看起来需要强制的事情,但它仍然会泄漏。

{-# LANGUAGE BangPatterns #-}

import Data.Conduit
import Data.Conduit.List
import Control.Monad.IO.Class

-- | 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 :: Int) <= 1
then do
liftIO $ act t v
yield v
skipN n (succ t)
else do
yield v
skipN (pred c) (succ t)

main :: IO ()
main = unfold (\b -> b `seq` Just (b, b+1)) 1
$= progress 100000 (\_ b -> print b)
$$ fold (\_ _ -> ()) ()

另一方面,
main = unfold (\b -> b `seq` Just (b, b+1)) 1 $$ fold (\_ _ -> ()) ()

不会泄漏,所以 progress 中的某些内容确实似乎是问题所在。我看不出是什么。

编辑:泄漏只发生在 ghci 上!如果我编译一个二进制文件并运行它,就没有泄漏(我应该早点测试过这个......)

关于haskell - 为什么这会导致 Haskell Conduit 库中的内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24786039/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com