【问题标题】:Most efficient way to seek around in a large file在大文件中四处寻找的最有效方法
【发布时间】:2013-02-09 18:23:43
【问题描述】:

在 Haskell 中处理非常大的二进制文件的最有效方法是什么?

标准答案是将整个文件作为惰性 ByteString 读取,然后使用二进制数据包之类的东西在其上编写解析器。有几个问题...

首先,像 Binary 这样的库并不能真正处理解析失败,我明确地预计解析有时会失败。

其次,我没有解析整个文件内容。我将跳过其中的大部分内容。并且将千兆字节的数据从磁盘读取到 RAM 中,然后让垃圾收集器再次将其丢弃,这似乎相当低效。

与此相关,我需要能够判断我想要执行的跳过是否会使我脱离文件末尾(如果确实如此,则会出错)。

我可能还需要向后查找,或者可能需要查找文件中的特定字节偏移量,而惰性 ByteString 方法似乎不能很好地支持这一点。 (最终将整个文件保存在 RAM 中是非常危险的。)

当然,另一种方法是逐个读取单个字节,并与hSeek 命令交错。但现在的问题是,一次读取一个字节的文件效率如何?听起来它可能很慢。我不确定hSetBuffering 是否对此有影响。 (?)

当然还有mmap。但如果用于大文件,这似乎会吓坏虚拟内存系统。 (这很奇怪,考虑到这是它存在的全部目的......)

各位,我们怎么看?就 I/O 性能和代码可维护性而言,解决此问题的最佳方法是什么?

【问题讨论】:

  • hGetBufhGetBufNonBlockinghGetSome 怎么样?您可以读取比单个字节更大的 thunk。
  • 使用a mmap'd bytestring?然后索引只从内存中加载所需的块。
  • 我想知道这一切如何与hSetBuffering相互作用?

标签: performance haskell io


【解决方案1】:

我在使用 pdf 解析器时遇到了类似的问题。最初我使用iteratee 包(它supports random access)。 AFAIK 它是唯一支持随机 IO 的 IO 库。

我的current approach 基于io-streams 包。我发现它更方便。性能足够好,attoparsec 集成开箱即用,包括很多组合器。

这是一个如何使用iteratee 进行随机IO 的基本示例:

shum@shum-laptop:/tmp/shum$ cat test.hs 

import qualified  Data.Iteratee as I
import qualified Data.Attoparsec.Iteratee as I
import qualified Data.Attoparsec.Char8 as P
import Control.Monad.IO.Class
import System.Environment

main :: IO ()
main = do
  [file] <- getArgs
  flip I.fileDriverRandom file $ do
    I.seek 20
    num1 <- I.parserToIteratee P.number
    liftIO $ print num1
    I.seek 10
    num2 <- I.parserToIteratee P.number
    liftIO $ print num2
shum@shum-laptop:/tmp/shum$ cat in.data 
111111111
222222222
333333333
shum@shum-laptop:/tmp/shum$ runhaskell test.hs in.data 
333333333
222222222
shum@shum-laptop:/tmp/shum$

【讨论】:

  • 我对迭代技术不是很熟悉。我发现最近人们对它很感兴趣,也许这是解决我的问题的好方法,但是 iteratee 包本身的文档太少了,我无法弄清楚。
  • 我添加了一个例子。这是非常基本的,但应该给出想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多