Joe*_*oel 6 io haskell lazy-evaluation bytestring
我正在尝试使用Haskell处理大文件.我希望逐字节地浏览输入文件,并在字节之后生成输出字节.当然,我需要使用合理大小的块(几KB)来缓冲IO.我不能这样做,我需要你的帮助.
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
Run Code Online (Sandbox Code Playgroud)
我命名此文件TestCopy.hs,然后执行以下操作:
$ 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
$
Run Code Online (Sandbox Code Playgroud)
我的问题:5MB和10MB文件之间存在巨大差异.我希望表演在输入文件的大小上是线性的.请问我做错了什么,我怎样才能做到这一点?我不介意使用惰性字节串或其他任何东西,只要它有效,但它必须是标准的ghc库.
精确度:这是一个大学项目.而且我不是要复制文件.该doSomeStuff函数应执行我必须自定义的压缩/解压缩操作.
对于分块输入处理,我会使用枚举器包.
import Data.Enumerator
import Data.Enumerator.Binary (enumFile)
Run Code Online (Sandbox Code Playgroud)
我们使用bytestrings
import Data.ByteString as BS
Run Code Online (Sandbox Code Playgroud)
和IO
import Control.Monad.Trans (liftIO)
import Control.Monad (mapM_)
import System (getArgs)
Run Code Online (Sandbox Code Playgroud)
您的主要功能可能如下所示:
main =
do (filepath:_) <- getArgs
let destination
run_ $ enumFile filepath $$ writeFile (filepath ++ ".cpy")
Run Code Online (Sandbox Code Playgroud)
enumFile每个块读取4096个字节并将它们传递给writeFile,writeFile将其写下来.
enumWrite定义为:
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
Run Code Online (Sandbox Code Playgroud)
如您所见,step函数采用块字节串并将它们附加到目标文件.这些块具有Stream BS.Bytestring类型,其中Stream定义为:
data Stream a = Chunks [a] | EOF
Run Code Online (Sandbox Code Playgroud)
在EOF步骤终止,屈服().
要对此进行更详细的阅读,我个人推荐Michael Snoymans 教程
$ 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
Run Code Online (Sandbox Code Playgroud)
这是一个很大的进步.现在,为了实现您的折叠,您可能想要编写一个Enumeratee,它用于转换输入流.幸运的是,枚举器包中已经定义了一个map函数,可以根据需要进行修改,即可以修改它以进行状态转换.
您以相反的顺序构造wordsList并在之后反转它.我认为差异列表做得更好,因为附加只需要O(1)时间,因为追加只是一个函数组合.我不确定他们是否需要更多的空间.这是差异列表的草图:
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 []
Run Code Online (Sandbox Code Playgroud)
可能不再需要这个答案,但我为了完整性而添加了它.
我认为这是在 haskell 中读取大文件的后续?从昨天。
尝试使用“-rtsopts -O2”而不是“-O”进行编译。
您声称“我想逐个字节地浏览输入文件,并生成一个又一个字节的输出”。但您的代码在尝试创建任何输出之前会读取整个输入。这并不能很好地代表目标。
在我的系统中,我看到“ghc -rtsopts --make -O2 b.hs”给出
(! 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
Run Code Online (Sandbox Code Playgroud)
现在对我来说看起来是线性的。
| 归档时间: |
|
| 查看次数: |
1876 次 |
| 最近记录: |