假设我有几个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
是惰性的,它会避免将整个文件读入内存。但在使用valgrind
运行两个脚本时,两者的内存使用情况相似。所以我的脚本可能有误,或者valgrind
有误。我使用以下命令编译脚本:
ghc --make test.hs -prof
我错过了什么?额外问题:我在 Stack Overflow 上看到很多提到 Haskell 中的惰性 IO 实际上是一件坏事。 我如何/为什么使用严格的 IO?
更新:
所以看起来我错读了 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
和 hGetContents
都是惰性的。后续问题:
为什么我看到堆上分配了7GB的内存?对于一个读取44MB文件的脚本来说,这似乎非常高。
后续问题的更新:
看起来在Haskell中分配几个GB的堆内存并不是不寻常的,所以没有什么可担心的。使用
ByteString
而不是String
可以大大降低内存使用量: 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)
putStrLn
输出之前不需要构建整个 “unlines” 字符串吗?我会尝试使用像Control.Monad.forM_ (filter (isPrefixOf "import") contents) $ putStrLn
这样的代码。但这只是我的猜测。 - Riccardo T.unlines
可以进行惰性求值。在ghci
中尝试运行putStr $ unlines $ map show [1..]
。 - ephemient在堆中分配了7,807,461,320字节
。 - Vlad the Impala