假设我有几个200mb +文件,我想要通过.我怎么在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
Run Code Online (Sandbox Code Playgroud)
这会在解析之前将整个文件读入内存.然后我去了:
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
Run Code Online (Sandbox Code Playgroud)
我以为既然hGetContents很懒,就会避免将整个文件读入内存.但是,运行这两个脚本valgrind显示两者的内存使用量相似.所以要么我的脚本错了,要么valgrind错了.我使用编译脚本
ghc --make test.hs -prof
Run Code Online (Sandbox Code Playgroud)
我错过了什么?奖金问题:我看到很多关于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)
Run Code Online (Sandbox Code Playgroud)
重要的是101,888 bytes maximum residency,在任何给定的点上我的脚本最多使用101 kb的内存.我翻阅的文件是44 MB.所以我认为判决结果是:readFile并且hGetContents都是懒惰的.
后续问题:
为什么我在堆上看到7gb的内存分配?对于一个读取44 MB文件的脚本来说,这似乎非常高.
更新后续问题
看起来堆上分配的几gb内存对于Haskell来说并不典型,所以不必担心.使用ByteStrings而不是Strings会占用大量内存:
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)
Run Code Online (Sandbox Code Playgroud)
请不要使用普通String的(特别是在处理> 100m文件时).只需用ByteString's(或Data.Text)替换它们:
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad
import System.Environment
import qualified Data.ByteString.Lazy.Char8 as B
main = do
filename <- liftM getArgs
contents <- liftM B.lines $ B.readFile filename
B.putStrLn . B.unlines . filter (B.isPrefixOf "import") $ contents
Run Code Online (Sandbox Code Playgroud)
我打赌,这会快几倍.
UPD:关于你的后续问题.
切换到字节串时,分配的内存量与魔术加速密切相关.
由于String它只是一个通用列表,它需要额外的内存Char:指向下一个元素,对象头等的指针.所有这些内存需要分配然后收回.这需要大量的计算能力.
另一方面,ByteString是一个块列表,即连续的内存块(我认为,每个不少于64个字节).这大大减少了分配和集合的数量,并且还改善了缓存局部性.
| 归档时间: |
|
| 查看次数: |
1399 次 |
| 最近记录: |