以下程序的内存分析表明,noleak 函数在恒定内存中运行,而leak 函数以线性方式泄漏内存。 dflemstr 表示这可能是由于 RWST 导致无限的分配链。是这种情况吗?还有其他解决方案吗?我实际上不需要 Writer monad。
环境:
ARCH 64 位上的 GHC 7.8.3
ghc Pipe.hs -o Pipe -prof
import Control.Concurrent (threadDelay)
import Control.Monad (forever)
import Pipes
import Control.Monad.Trans.RWS.Strict
main = leak
effectLeak :: Effect (RWST () () () IO) ()
effectLeak =
(forever $ do
liftIO . threadDelay $ 10000 * 1
yield "Space") >->
(forever $ do
text <- await
yield $ text ++ (" leak" :: String)) >->
(forever $ do
text <- await
liftIO . print $ text
)
effectNoleak :: Effect IO ()
effectNoleak =
(forever $ do
lift . threadDelay $ 10000 * 1
yield "Space") >->
(forever $ do
text <- await
yield $ text ++ (" leak" :: String)) >->
(forever $ do
text <- await
lift . print $ text
)
leak = (\e -> runRWST e () ()) . runEffect $ effectLeak
noleak = runEffect $ effectNoleak
最佳答案
Zeta 是对的,空间泄漏是由于 WriterT
造成的。无论您使用什么幺半群,WriterT
和 RWST
(“严格”版本和惰性版本)总是会泄漏空间。
我对此写了一个更长的解释 here ,但总结如下:不泄漏空间的唯一方法是使用 StateT
monad 模拟 WriterT
,其中 tell
使用严格的模拟放置
,如下所示:
newtype WriterT w m a = WriterT { unWriterT :: w -> m (a, w) }
instance (Monad m, Monoid w) => Monad (WriterT w m) where
return a = WriterT $ \w -> return (a, w)
m >>= f = WriterT $ \w -> do
(a, w') <- unWriterT m w
unWriterT (f a) w'
runWriterT :: (Monoid w) => WriterT w m a -> m (a, w)
runWriterT m = unWriterT m mempty
tell :: (Monad m, Monoid w) => w -> WriterT w m ()
tell w = WriterT $ \w' ->
let wt = w `mappend` w'
in wt `seq` return ((), wt)
这基本上相当于:
type WriterT = StateT
runWriterT m = runStateT m mempty
tell w = do
w' <- get
put $! mappend w w'
关于haskell - RWST 管道中的空间泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25280852/