Monadic在恒定空间中折叠

rya*_*hza 9 performance haskell

如何在恒定空间中使用monadic动作折叠惰性列表?我试图解决的问题是聚合一个大文件,我相信为了性能我需要可变性.我有一个使用可变向量在ST工作的实现,但它使用了太多的内存.以下是我正在尝试的一个例子.我还对Conduit进行了简要的实验,但似乎没有提供任何改进.

ST forM_:

import Control.Monad (forM_)
import Control.Monad.ST.Trans as STT
import Control.Monad.Identity as Identity

testST :: Int
testST = do
  Identity.runIdentity $ STT.runST $ do
    a <- STT.newSTRef 0
    forM_ [1..10000000] (\x -> do
        a' <- STT.readSTRef a
        STT.writeSTRef a (a' + x)
      )
    STT.readSTRef a
Run Code Online (Sandbox Code Playgroud)

导管:

import Data.Conduit (($=),(=$),($$))
import qualified Data.Conduit as C
import qualified Data.Conduit.List as CL

testCL :: IO Int
testCL = CL.sourceList [1..10000000] $$ CL.foldM (\a x -> return (a + x)) 0
Run Code Online (Sandbox Code Playgroud)

Dan*_*ner 15

问题不在于折叠,而在于折叠体.这个程序分配了很多:

testST = runST $ do
    ref <- newSTRef 0
    forM_ [1..10000000] $ \x -> do
         val <- readSTRef ref
         writeSTRef ref (val + x)
    readSTRef ref
Run Code Online (Sandbox Code Playgroud)

这个程序,其唯一的区别是writeSTRef在线,几乎没有分配:

testST = runST $ do
    ref <- newSTRef 0
    forM_ [1..10000000] $ \x -> do
        val <- readSTRef ref
        writeSTRef ref $! val + x
    readSTRef ref
Run Code Online (Sandbox Code Playgroud)

两段代码之间的区别是对正在发生的事情的一个很好的提示:在前者中,您正在创建对具有10000000层应用程序的深层嵌套thunk的引用+; 而后者在每一步都会使thunk变平.

顺便说一下,这个常见的陷阱在文档中明确地modifySTRef提到.