RWST管道中的空间泄漏 [英] Space leak in Pipes with RWST
问题描述
以下程序的内存分析显示,noleak函数在常量内存中运行,而泄漏函数以线性方式泄漏内存。 dflemstr表示这可能是由于RWST导致无限的分配链。这是什么情况,还有其他解决方案吗?我实际上不需要Writer monad。
环境:
GHC 7.8.3在ARCH 64位
p>ghc Pipe.hs -o Pipe -prof
import Control.Concurrent (threadDelay)
导入Control.Monad(永久)
导入管道
导入Control.Monad.Trans.RWS.Strict
main = leak
effectLeak :: Effect(RWST()()()IO)()
effectLeak =
(forever $ do
liftIO。threadDelay $ 10000 * 1
收益空间)> - >
(forever $ do
text< - await
yield $ text ++(leak:: String))> - >
(永远$ do
text< - 等待
liftIO。print $ text
)
effectNoleak :: Effect IO()
effectNoleak =
(forever $ do
lift。threadDelay $ 10000 * 1
yieldSpace)> - >
(forever $ do
text< - await
yield $ text ++(leak:: String))> - >
(永远$ do
text< - 等待
lift。print $ text
)
leak =(\e - > runRWST e ()())。 runEffect $ effectLeak
noleak = runEffect $ effectNoleak
看起来像 RWST 的一部分 Writer 部分是实际的罪魁祸首:
instance(Monoid w,Monad m)=> Monad(RWST r w s m)其中
返回a = RWST $ \ _ s - >返回(a,s,mempty)
m>> = k = RWST $ \\ r s - > (b,s'',w')< - runRWST(ka)r s'
return(b,s',w)< - runRWST mrs
s'',w`mappend` w') - mappend
fail msg = RWST $ \ _ _ - - >失败msg
正如您所看到的,作者使用普通的 mappend
。由于
(,,)
在其参数中并不严格,因此 w`mappend` w'
会生成一系列thunk ,甚至很难 Monoid
()
相当微不足道:
实例Monoid()其中
- 是否应该严格?
mempty =()
_`mappend` _ =()
mconcat _ =()
为了解决这个问题,您需要在元组中添加严格性 w`mappend` w'
:
let wt = w`mappend` w'
wt`seq` return(b,s',wt)
但是,如果您不需要 Writer
,那么您可以只需使用 ReaderT r(StateT st m)
来代替:
import控制.Monad.Trans.Reader
import Control.Monad.Trans.State.Strict
type RST r st m = ReaderT r(StateT st m)
runRST :: Monad m => RST r st m a - > r - > st - > m(a,st)
runRST rst st st = flip runStateT st。 flip runReaderT r $ rst
然而,如果这会迫使你 mtl $取而代之的是c $ c> package
。代码将保持不变,但在这种情况下导入将如下
import Control.Monad.Reader
import Control.Monad.State.Strict
A memory analysis of the following program shows that the noleak functions runs in constant memory while the leak function leaks memory in a linear fashion. dflemstr indicated that this might be due to RWST causing an infinite chain of allocations. Is this the case and what other solutions exists? I actually dont need the Writer monad.
Environment:
GHC 7.8.3 on ARCH 64 bit
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
It seems like the Writer
part of RWST
is actually the culprit:
instance (Monoid w, Monad m) => Monad (RWST r w s m) where
return a = RWST $ \ _ s -> return (a, s, mempty)
m >>= k = RWST $ \ r s -> do
(a, s', w) <- runRWST m r s
(b, s'',w') <- runRWST (k a) r s'
return (b, s'', w `mappend` w') -- mappend
fail msg = RWST $ \ _ _ -> fail msg
As you can see, the writer uses a plain mappend
. Since (,,)
isn't strict in its arguments, w `mappend` w'
builds a series of thunks, even tough the Monoid
instance of ()
is rather trivial:
instance Monoid () where
-- Should it be strict?
mempty = ()
_ `mappend` _ = ()
mconcat _ = ()
In order to fix this, you need to add strictness to w `mappend` w'
in the tuple:
let wt = w `mappend` w'
wt `seq` return (b, s'', wt)
However, if you don't need the Writer
anyway, you can simply use ReaderT r (StateT st m)
instead:
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State.Strict
type RST r st m = ReaderT r (StateT st m)
runRST :: Monad m => RST r st m a -> r -> st -> m (a,st)
runRST rst r st = flip runStateT st . flip runReaderT r $ rst
However, given that this will force you to lift
the computations to the correct monad, you might want to use the mtl
package instead. The code will stay the same, but the imports will be the following in this case
import Control.Monad.Reader
import Control.Monad.State.Strict
这篇关于RWST管道中的空间泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!