Haskell懒惰的字节串字不懒惰?

时间:2013-09-02 10:26:12

标签: haskell bytestring long-lines 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))

这个程序的问题是它将整个文件读入内存。没有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))

它只使用少量恒定的内存,但当然速度极慢(大约慢25倍)。

只有读取非常大的行的程序才会出现此问题。如果输入分布在多个小行上,ByteString将按预期执行。

有什么方法吗?

2 个答案:

答案 0 :(得分:6)

懒惰元组的使用存在次优。这更好地改写为:

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

所以你得到一个严格的,未装箱的累加器。但是,你最好将整个事情写成增量左折。这就是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)

正如你所说,它保留了文件。

enter image description here

那么保留记忆力是什么?一个线索是带有opt的foldl。你把它传给了一个懒惰的元组。 foldllazy in its accumulator

因此,您只需构建一系列未评估的+操作。 opt上的爆炸模式没有区别,因为foldl从不评估其累加器。只有当你最终在结束时检查结果时,整个事情才会崩溃。

这是一个经典的空间泄漏。所以:

  • 使用foldl' - 在累加器中它是严格的
  • 完全避免使用中间列表(使用readInt + unfoldr)。
  • 如果您必须遍历列表,请在累加器上使用严格元组以获得更好的性能。

答案 1 :(得分:1)

map (fst.fromJust.readInt) cont

不应该是

main = do
    cont <- getContents
    putStrLn $ show $ snd $ foldl opt (0,0) $
               unfoldr (readInt . dropWhile isSpace) cont