假设我有几个 200mb+ 的文件想要 grep 遍历。我该如何在 Haskell 中做到这一点?
这是我的初始程序:
import Data.List
import Control.Monad
import System.IO
import System.Environment
main = do
filename <- liftM head getArgs
contents <- liftM lines $ readFile filename
putStrLn . unlines . filter (isPrefixOf "import") $ contents
这会在解析之前将整个文件读入内存。
然后我就这样做了:
import Data.List
import Control.Monad
import System.IO
import System.Environment
main = do
filename <- liftM head getArgs
file <- (openFile filename ReadMode)
contents <- liftM lines $ hGetContents file
putStrLn . unlines . filter (isPrefixOf "import") $ contents
我以为自从hGetContents
是懒惰,它将避免将整个文件读入内存 http://book.realworldhaskell.org/read/io.html#io.lazy。但是在下面运行这两个脚本valgrind
两者的内存使用情况相似。所以要么我的脚本是错误的,要么valgrind
是错的。我使用编译脚本
ghc --make test.hs -prof
我缺少什么?额外问题:我看到很多人提到 Haskell 中的 Lazy IO 实际上是一件坏事。我如何/为什么要使用严格 IO?
Update:
所以看起来我对 valgrind 的理解是错误的。使用+RTS -s
,这就是我得到的:
7,807,461,968 bytes allocated in the heap
1,563,351,416 bytes copied during GC
101,888 bytes maximum residency (1150 sample(s))
45,576 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)
Generation 0: 13739 collections, 0 parallel, 2.91s, 2.95s elapsed
Generation 1: 1150 collections, 0 parallel, 0.18s, 0.18s elapsed
INIT time 0.00s ( 0.00s elapsed)
MUT time 2.07s ( 2.28s elapsed)
GC time 3.09s ( 3.13s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 5.16s ( 5.41s elapsed)
重要的一行是101,888 bytes maximum residency
,它表示在任何给定点我的脚本最多使用 101 kb 内存。我正在查找的文件有 44 mb。所以我认为判决是:readFile
and hGetContents
两人都很懒。
后续问题:
为什么我看到堆上分配了 7GB 内存?对于读取 44 MB 文件的脚本来说,这似乎非常高。
更新后续问题
看起来在堆上分配几 GB 的内存对于 Haskell 来说并不罕见,所以没有理由担心。使用ByteString
s 而不是String
s 使内存使用量下降很多:
81,617,024 bytes allocated in the heap
35,072 bytes copied during GC
78,832 bytes maximum residency (1 sample(s))
26,960 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)