Предположим, у меня есть несколько файлов размером 200 Мбайт, которые я хочу пропустить. Как мне это сделать в Haskell?Разбор больших файлов журнала в 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
ленив, it will avoid reading the whole file into memory. Но запуск обоих сценариев под valgrind
показал одинаковое использование памяти для обоих. Так что либо мой сценарий ошибочен, либо valgrind
ошибочен. Я скомпилирую сценарии с помощью
ghc --make test.hs -prof
Что мне не хватает? Бонусный вопрос: я вижу много упоминаний о том, как Lazy IO в Haskell на самом деле плохо. Как/почему я должен использовать строгий 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 кбайт памяти в большинстве. Файл, который я просматривал, составлял 44 мб. Поэтому я считаю, что вердикт: readFile
и hGetContents
- оба ленивые.
последующий вопрос:
Почему я вижу 7GB памяти выделяется в куче? Это кажется очень высоким для скрипта, который читается в файле размером 44 МБ.
Обновления последующего вопроса
Похоже, несколько гб памяти, выделенная в куче не является нетипичной для Haskell, так что нет причин для беспокойства. Использование 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)
Хмм, вы уверены, что вам не нужно создавать целую строку 'unlines', прежде чем писать ее с помощью' putStrLn'? Я бы попытался что-то вроде «Control.Monad.forM_ (содержимое фильтра (isPrefixOf« импорт ») $ putStrLn'. Однако это предположение. –
@ Riccardo: Нет, 'unlines' можно оценить лениво. Попробуйте 'putStr $ unlines $ map show [1 ..]' в 'ghci'. – ephemient
Делает -O2 волшебно решает проблему? – gspr