ver titulo propiedades nombre modificar metadatos etiquetas editar como cambiar archivos archivo io lazy-evaluation bytestring haskell

titulo - IO sobre archivos grandes en haskell: problema de rendimiento



modificar tags mkv (2)

Para el procesamiento de entrada fragmentada, usaría el paquete del enumerador .

import Data.Enumerator import Data.Enumerator.Binary (enumFile)

Usamos cadenas de bytes

import Data.ByteString as BS

y IO

import Control.Monad.Trans (liftIO) import Control.Monad (mapM_) import System (getArgs)

Su función principal podría ser similar a la siguiente:

main = do (filepath:_) <- getArgs let destination run_ $ enumFile filepath $$ writeFile (filepath ++ ".cpy")

enumFile lee 4096 bytes por fragmento y los pasa a writeFile, que lo anota.

enumWrite se define como:

enumWrite :: FilePath -> Iteratee BS.ByteString IO () enumWrite filepath = do liftIO (BS.writeFile filepath BS.empty) -- ensure the destination is empty continue step where step (Chunks xs) = do liftIO (mapM_ (BS.appendFile filepath) xs) continue step step EOF = yield () EOF

Como puede ver, la función de paso toma trozos de cadenas de bytes y los agrega al archivo de destino. Estos fragmentos tienen el tipo Stream BS.Bytestring, donde Stream se define como:

data Stream a = Chunks [a] | EOF

En un paso EOF termina, produciendo ().

Para tener una lectura mucho más elaborada sobre esto yo personalmente recomiendo el tutorial de Michael Snoymans

Los números

$ time ./TestCopy 5MB ./TestCopy 5MB 2,91s user 0,32s system 96% cpu 3,356 total $ time ./TestCopy2 5MB ./TestCopy2 5MB 0,04s user 0,03s system 93% cpu 0,075 total

Eso es una gran mejora. Ahora, para implementar su pliegue, probablemente desee escribir un Enumeratee, que se utiliza para transformar una secuencia de entrada. Afortunadamente, ya existe una función de mapa definida en el paquete del enumerador, que puede modificarse para su necesidad, es decir, puede modificarse para transferir el estado.

En la construcción del resultado intermedio

Usted construye la Lista de palabras en orden inverso y la invierte después. Creo que las listas de diferencias hacen un mejor trabajo, porque los anexos toman solo O (1) tiempo debido al hecho de que agregar es solo una composición de función. No estoy seguro si toman más espacio sin embargo. Aquí hay un boceto de las listas de diferencias:

type DList a = [a] -> [a] emptyList :: DList a emptyList = id snoc :: DList a -> a -> DList a snoc dlist a = dlist . (a:) toList :: DList a -> [a] toList dlist = dlist []

Esta respuesta probablemente ya no sea necesaria, pero la agregué para completarla.

Estoy intentando trabajar con archivos grandes usando Haskell. Me gustaría explorar un byte de archivo de entrada después de un byte y generar un byte de salida después del byte. Por supuesto, necesito que el IO se almacene con bloques de tamaño razonable (unos pocos KB). No puedo hacerlo, y necesito tu ayuda, por favor.

import System import qualified Data.ByteString.Lazy as BL import Data.Word import Data.List main :: IO () main = do args <- System.getArgs let filename = head args byteString <- BL.readFile filename let wordsList = BL.unpack byteString let foldFun acc word = doSomeStuff word : acc let wordsListCopy = foldl'' foldFun [] wordsList let byteStringCopy = BL.pack (reverse wordsListCopy) BL.writeFile (filename ++ ".cpy") byteStringCopy where doSomeStuff = id

TestCopy.hs este archivo TestCopy.hs , luego hago lo siguiente:

$ ls -l *MB -rwxrwxrwx 1 root root 10000000 2011-03-24 13:11 10MB -rwxrwxrwx 1 root root 5000000 2011-03-24 13:31 5MB $ ghc --make -O TestCopy.hs [1 of 1] Compiling Main ( TestCopy.hs, TestCopy.o ) Linking TestCopy ... $ time ./TestCopy 5MB real 0m5.631s user 0m1.972s sys 0m2.488s $ diff 5MB 5MB.cpy $ time ./TestCopy 10MB real 3m6.671s user 0m3.404s sys 1m21.649s $ diff 10MB 10MB.cpy $ time ./TestCopy 10MB +RTS -K500M -RTS real 2m50.261s user 0m3.808s sys 1m13.849s $ diff 10MB 10MB.cpy $

Mi problema: hay una gran diferencia entre un archivo de 5 MB y uno de 10 MB. Me gustaría que las actuaciones sean lineales en el tamaño del archivo de entrada. Por favor, ¿qué estoy haciendo mal y cómo puedo lograrlo? No me importa usar cadenas de bytes vagas o cualquier otra cosa, siempre y cuando funcione, pero tiene que ser una biblioteca estándar de ghc.

Precisión: es para un proyecto universitario. Y no estoy tratando de copiar archivos. La función doSomeStuff realizará acciones de compresión / descompresión que tengo que personalizar.


Supongo que este es un seguimiento de la lectura de archivos grandes en Haskell. de ayer.

Intente compilar con "-rtsopts -O2" en lugar de solo "-O".

Usted afirma que "me gustaría explorar un byte de archivo de entrada después de un byte y generar un byte de salida después de un byte". pero su código lee la entrada completa antes de intentar crear cualquier salida. Esto no es muy representativo del objetivo.

Con mi sistema veo "ghc -rtsopts --make -O2 b.hs" dando

(! 741)-> time ./b five real 0m2.789s user 0m2.235s sys 0m0.518s (! 742)-> time ./b ten real 0m5.830s user 0m4.623s sys 0m1.027s

Que ahora parece lineal para mí.