次のようなモジュールがあるとします。
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」関数はバイト文字列プロデューサーであり、解析を折りたたんで何らかの結果を生成することに関心があります。
次の 2 つのプログラムは、バイト文字列を一連の JSON int として解析することにより、バイト文字列の最大値を見つけるという問題に取り組むさまざまな方法を示しています。
プログラム 1:
module Main where
import Explosion (produceInts')
import Pipes.Prelude (fold)
main :: IO ()
main = do
biggest <- fold max 0 id produceInts'
print $ show biggest
プログラム 2:
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(^.)
から使用します。使用グラフは以下。どちらのプログラムも、時間の約 70% をガベージ コレクションに費やしています。fmap
zoom
私は何か間違ったことをしていますか?Prelude 関数max
は十分に厳密ではありませんか? ライブラリ関数が悪いのか、それともライブラリの使い方が間違っているのかわかりません! (多分後者です。)
完全を期すために、私が話していることを直接確認したい場合は、複製して実行できるgit リポジトリを次に示します。2 つのプログラムのメモリ使用量は次のとおりです。cabal install