haskell中大文件的IO:性能问题

haskell中大文件的IO:性能问题,io,lazy-evaluation,bytestring,haskell,Io,Lazy Evaluation,Bytestring,Haskell,我试图用Haskell处理大文件。我想一个字节一个字节地浏览输入文件,并一个字节一个字节地生成输出文件。当然,我需要用大小合理的块(几KB)缓冲IO。我做不到,我需要你的帮助 import System import qualified Data.ByteString.Lazy as BL import Data.Word import Data.List main :: IO () main = do args <- Sys

我试图用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
我的问题是:5MB文件和10MB文件之间存在巨大差异。我希望性能与输入文件的大小成线性关系。请问我做错了什么,我怎样才能做到这一点?我不介意使用lazy ByTestring或其他任何东西,只要它能工作,但它必须是标准的ghc库


精确性:这是一个大学项目。我不想复制文件。
doSomeStuff
功能将执行我必须自定义的压缩/解压缩操作。

我认为这是昨天的后续操作

尝试使用“-rtsopts-O2”而不是仅使用“-O”进行编译

您声称“我希望一个字节接一个字节地浏览输入文件,并生成一个字节接一个字节的输出。”但您的代码在尝试创建任何输出之前读取整个输入。这并不能很好地代表目标

在我的系统中,我看到“ghc-rtsopts-make-O2b.hs”给出


现在我觉得它是线性的。

对于分块输入处理,我会使用这个包

我们使用bytestring

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")
如您所见,step函数获取bytestring块并附加它们 到目标文件。这些块的类型为Stream BS.Bytestring,其中 流定义为:

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
data Stream a = Chunks [a] | EOF
在EOF步骤结束时,产生()

为了更详细地了解这一点,我个人推荐迈克尔 势利小人

数字 这是相当大的进步。现在,为了实现折叠,您可能需要编写一个枚举,用于转换输入流。幸运的是,枚举器包中已经定义了一个映射函数,可以根据您的需要对其进行修改,也就是说,可以将其修改为结转状态

论中间结果的构建 您可以按相反的顺序构建单词列表,然后将其反转。我认为做得更好,因为追加只需要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 []

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

ByteString的
pack
unpack
是非常昂贵的操作。您不能直接使用ByteString
doSomeStuff
吗?注意:lazy ByteString在内部是“缓冲”的,这对于您的任务可能已经足够了。我只是在没有打包和解包的情况下尝试了它,直接在ByteString上工作,结果甚至更长,我仍然有5MB和10MB之间的巨大差异。也许你可以在某个地方发布一个完整的代码示例来演示这个问题?@Ed'ka,但我就是这么做的。上面TestCopy.hs的工作代码演示了这个问题,因为10MB的文件在3分钟内就被复制了。
cabal unpack blaze builder
——现在您有了源代码,可以将您需要的任何文件直接移动到repo中。是的,但是ByteString.Lazy包应该是懒惰的,至少我认为是这样。无论如何,我不介意使用另一种技术或库,只要它是标准的。我的编译器无法识别您向我建议的选项。我的个人版本是6.12.1,而我必须使用的大学服务器上的版本是6.8.2。如果我卸载ghc并重新安装最新版本,您认为我会得到正常结果吗?PS:我昨天的问题是关于同一个项目,但另一个问题,你已经解决了,谢谢。@Joel-
-rtsopts
标志只对ghc-7有用,所以请不要使用它<代码>-O2应该是标准的。再一次,你是对的。我按照你的解释,用内部缓冲区重新编写了我的整个IO库。事实上,我根本没有缓冲任何东西。我可能永远也不会明白-懒惰IO-意味着什么。。。但我会在考试中取得成功,因为我的haskell压缩实用程序只比做同样事情的C程序慢5倍。Chris Kuklewicz,你帮了大忙,非常感谢你分享你的知识。嗯,根据GHC文档,“目前,-O2不可能产生比-O更好的代码”(c)writeFile和enumWrite是同一函数的两个名称,对吗?
data Stream a = Chunks [a] | EOF
$ 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
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 []