簡體   English   中英

管道中的空間泄漏與RWST

[英]Space leak in Pipes with RWST

以下程序的存儲器分析表明,noleak函數在常量存儲器中運行,而泄漏函數以線性方式泄漏存儲器。 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 無論你使用什么樣的monoid, WriterTRWST (“嚴格”和懶惰版本)總是會泄漏空間。

在這里寫了一個更長的解釋,但這里是摘要:不泄漏空間的唯一方法是使用StateT monad模擬WriterT ,其中tell使用嚴格的put模擬,如下所示:

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'

看起來RWSTWriter部分實際上是罪魁禍首:

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

如你所見,作者使用普通的mappend 因為(,,)在它的參數中並不嚴格,所以w `mappend` w'構建了一系列的thunk,即使是()Monoid實例也很瑣碎

instance Monoid () where
        -- Should it be strict?
        mempty        = ()
        _ `mappend` _ = ()
        mconcat _     = ()

為了解決這個問題,你需要在元組中添加w `mappend` w'嚴格性:

        let wt = w `mappend` w'
        wt `seq` return (b, s'', wt) 

但是,如果您還不需要Writer ,您只需使用ReaderT r (StateT st m)

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

但是,鑒於這將迫使您將計算lift到正確的monad,您可能希望使用mtl 代碼將保持不變,但在這種情況下導入將是以下內容

import Control.Monad.Reader
import Control.Monad.State.Strict

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM