use*_*083 3 haskell long-lines bytestring lazy-io
我有以下Haskell程序用于计算整数字符串的最大和子串:
{-# LANGUAGE BangPatterns #-} {-# OPTIONS_GHC -O2 #-}
import Data.Functor
import Data.Maybe
import Data.ByteString.Lazy.Char8 (getContents,lines,readInt,words)
import Prelude hiding (getContents,words,lines)
main = do
cont <- words <$> getContents
putStrLn $ show $ snd $ foldl opt (0,0) $ map (fst.fromJust.readInt) cont
opt (!c,!m) x = (max 0 (c+x),max m (c+x))
Run Code Online (Sandbox Code Playgroud)
该程序的问题在于它将整个文件读入内存.没有BytesString的相应程序没有这个问题:
{-# LANGUAGE BangPatterns #-} {-# OPTIONS_GHC -O2 #-}
import Data.Functor
import Data.Maybe
main = do
cont <- words <$> getContents
putStrLn $ show $ snd $ foldl opt (0,0) $ map read cont
opt (!c,!m) x = (max 0 (c+x),max m (c+x))
Run Code Online (Sandbox Code Playgroud)
它只使用少量恒定的内存,但当然速度极慢(大约慢25倍).
只有读取非常大的行的程序才会出现此问题.如果输入分布在多个小行上,ByteString将按预期执行.
有没有办法解决?
惰性元组的使用存在次优.这更好地重写为:
main = do
cont <- words <$> getContents
putStrLn $ show $ sndT $ foldl opt (T 0 0) $ map (fst.fromJust.readInt) cont
sndT :: T -> Int
sndT (T _ m) = m
opt (T c m) x = T (max 0 (c+x)) (max m (c+x))
data T = T {-# UNPACK #-} !Int {-# UNPACK #-}!Int
Run Code Online (Sandbox Code Playgroud)
所以你得到一个严格的,未装箱的累加器.但是,你最好将整个事情写成增量左折.这就是为什么readInt在第二个参数中返回剩余的输入.不需要总和.地图.单词管道.
您提交的版本泄漏了空间.在大文件上运行,它使用与文件大小成比例的堆(在640k条目上).
$ time ./A +RTS -p -s -K50M < input.txt.2
346882
326,337,136 bytes allocated in the heap
302,321,732 bytes copied during GC
82,617,772 bytes maximum residency (8 sample(s))
1,466,500 bytes maximum slop
149 MB total memory in use (0 MB lost due to fragmentation)
%GC time 63.8% (63.9% elapsed)
Run Code Online (Sandbox Code Playgroud)
正如你所说,它保留了文件.

什么是保留记忆?一个线索是折叠带opt.你把它传给了一个懒惰的元组.并且在它的累加器中foldl是懒惰的.
所以你只是建立一长串未经评估的+业务.爆炸模式opt没有区别,因为foldl永远不会评估它的累加器.只有当你最终在最后检查结果时,整个事情才会崩溃.
这是一个经典的空间泄漏.所以: