gpt4 book ai didi

Haskell : Performance issue 中大文件的 IO

转载 作者:行者123 更新时间:2023-12-04 13:27:43 25 4
gpt4 key购买 nike

我正在尝试使用 Haskell 处理大文件。我想逐字节浏览输入文件,并逐字节生成输出。当然,我需要用合理大小(几 KB)的 block 来缓冲 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

我将此文件命名为 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
$

我的问题: 5MB 和 10 MB 文件之间存在巨大差异。我希望性能与输入文件的大小成线性关系。请问我做错了什么,我该如何做到这一点?我不介意使用惰性字节串或其他任何东西,只要它有效,但它必须是标准的 ghc 库。

Precision:这是一个大学项目。而且我不是要复制文件。 doSomeStuff函数应执行我必须自定义的压缩/解压缩操作。

最佳答案

对于分 block 输入处理,我将使用 enumerator包裹。

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

我们使用字节串
import Data.ByteString as BS

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

您的主要功能可能如下所示:
main =
do (filepath:_) <- getArgs
let destination
run_ $ enumFile filepath $$ writeFile (filepath ++ ".cpy")

enumFile 读取每个 block 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

如您所见, step 函数采用字节串 block 并附加它们
到目标文件。这些 block 的类型为 Stream BS.Bytestring,其中
流定义为:
data Stream a = Chunks [a] | EOF

在 EOF 步骤终止时,产生 ()。

要对此进行更详细的阅读,我个人推荐迈克尔
斯诺伊曼 tutorial

号码
$ 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

这是一个很大的进步。现在,为了实现您的折叠,您可能需要编写一个 Enumeratee,它用于转换输入流。幸运的是,在 enumerator 包中已经定义了一个 map 函数,可以根据需要对其进行修改,即可以修改为继承状态。

关于中间结果的构建

您以相反的顺序构造 wordsList,然后将其反转。我认为 difference lists做得更好,因为附加只是一个函数组合,因此附加只需要 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 []

可能不再需要这个答案,但为了完整起见,我添加了它。

关于Haskell : Performance issue 中大文件的 IO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5419657/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com