假设我有一个这样的模块:
module Explosion where
import Pipes.Parse (foldAll, Parser, Producer)
import Pipes.ByteString (ByteString, fromLazy)
import Pipes.Aeson (DecodingError)
import Pipes.Aeson.Unchecked (decoded)
import Data.List (intercalate)
import Data.ByteString.Lazy.Char8 (pack)
import Lens.Family (view)
import Lens.Family.State.Strict (zoom)
produceString :: Producer ByteString IO ()
produceString = fromLazy $ pack $ intercalate " " $ map show [1..1000000]
produceInts ::
Producer Int IO (Either (DecodingError, Producer ByteString IO ()) ())
produceInts = view decoded produceString
produceInts' :: Producer Int IO ()
produceInts' = produceInts >> return ()
parseBiggest :: Parser ByteString IO Int
parseBiggest = zoom decoded (foldAll max 0 id)
'produceString' 函数是一个字节串生产者,我关心的是折叠解析以产生某种结果。
以下两个程序显示了通过将字节字符串解析为一系列 JSON 整数来解决在字节字符串中查找最大值问题的不同方法。
方案一:
module Main where
import Explosion (produceInts')
import Pipes.Prelude (fold)
main :: IO ()
main = do
biggest <- fold max 0 id produceInts'
print $ show biggest
方案二:
module Main where
import Explosion (parseBiggest, produceString)
import Pipes.Parse (evalStateT)
main :: IO ()
main = do
biggest <- evalStateT parseBiggest produceString
print $ show biggest
不幸的是,当我分析它们时,这两个程序总共占用了大约 200MB 的内存,我希望使用流解析器可以解决这个问题。第一个程序将大部分时间和内存 (> 70%) 花费在(^.)
Lens.Family 中,而第二个程序将其花费在fmap
Lens.Family.State.Strictzoom
中。使用图如下。这两个程序都将大约 70% 的时间用于垃圾收集。
难道我做错了什么?Prelude 功能是max
不是不够严格?我不知道库函数是否不好,或者我是否使用错误的库!(应该是后者。)
为了完整起见,这里有一个 git repocabal install
,如果你想亲眼看看我在说什么,你可以克隆并运行它,这里是两个程序的内存使用情况: