RWST管道中的空间泄漏 [英] Space leak in Pipes with RWST

查看:150
本文介绍了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


解决方案 div>

看起来像 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

然而,如果这会迫使你 lift 计算到正确的monad,您可能需要使用 mtl 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆