Supongamos que tengo varios archivos de 200mb + que quiero analizar. ¿Cómo haría esto en Haskell?Análisis de archivos de registro grandes en Haskell
Aquí está mi programa inicial:
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
Esto lee todo el archivo en memoria antes de analizar a través de él. Pasé luego con esto:
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
pensé que como hGetContents
es perezoso, it will avoid reading the whole file into memory. Pero ejecutar ambos scripts bajo valgrind
mostró un uso de memoria similar para ambos. Entonces, mi script está equivocado o valgrind
está mal. Recopilo los guiones usando
ghc --make test.hs -prof
¿Qué me falta? Pregunta de bonificación: veo muchas menciones en SO de cómo Lazy IO en Haskell es en realidad algo malo. ¿Cómo/por qué debería usar IO estricto?
Actualización:
por lo que parece que estaba equivocado en mi lectura de valgrind. Usando +RTS -s
, esto es lo que me sale:
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)
La línea importante es 101,888 bytes maximum residency
, que dice que en un momento dado mi guión estaba usando 101 KB de memoria como máximo. El archivo que estaba revisando era de 44 mb. Así que creo que el veredicto es: readFile
y hGetContents
son flojos.
Seguimiento pregunta:
¿Por qué veo 7 GB de memoria asignada en el montón? Eso parece muy alto para un script que está leyendo en un archivo de 44 mb.
actualización para el seguimiento pregunta
Parece que unos pocos GB de memoria asignada en el montón no es atípico para Haskell, por lo que no hay motivo de preocupación. Usando ByteString
s en lugar de String
s toma el uso de la memoria por mucho:
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)
Hum, ¿estás seguro de que no necesitas construir toda la cadena 'unlines' antes de escribirla con' putStrLn'? Me gustaría probar algo como 'Control.Monad.forM_ (filter (isPrefixOf" import)) contents) $ putStrLn'. Sin embargo, es solo una suposición. –
@Riccardo: No, 'unlines' se puede evaluar de forma perezosa. Pruebe 'putStr $ unlines $ map show [1 ..]' en 'ghci'. – ephemient
¿El -O2 resuelve mágicamente el problema? – gspr