15

Haskellで非常に大きなバイナリファイルを処理する最も効率的な方法は何ですか?

標準的な答えは、ファイル全体を遅延 ByteString として読み取り、バイナリ パケットのようなものを使用してその上にパーサーを書き込むことです。それにはいくつかの問題があります...

まず、Binary のようなライブラリは解析の失敗を実際には処理しませ

第 2 に、ファイルの内容全体を解析しているわけではありません。私はそれの大部分をスキップするつもりです。また、ディスクから RAM に数ギガバイトのデータを読み込んで、ガベージ コレクターに再び破棄させるのは、かなり効率が悪いように思われます。

それに関連して、実行したいスキップがファイルの末尾から外れるかどうかを判断できる必要があります (そうであればエラーになります)。

また、 backwardsをシークする必要がある場合や、ファイル内の特定のバイト オフセットまでシークする必要がある場合もありますが、これは遅延 ByteString アプローチでは十分にサポートされていないようです。(最終的にファイル全体が RAM に保持されるという深刻な危険があります。)

もちろん、別の方法として、個々のバイトをコマンドでインターリーブして 1 つずつ読み取ることもできhSeekます。しかし、問題は、一度に 1 バイトずつファイルを読み取るのがどれほど効率的かということです。それはまた、かなり遅くなる可能性があるように聞こえます。これに影響があるかどうかはわかりませんhSetBuffering。(?)

それからもちろんありmmapます。しかし、大きなファイルで使用すると、仮想メモリシステムが狂ってしまうようです。(それが存在するすべての目的であることを考えると、これは奇妙です...)

皆さん、どう思いますか?I/O パフォーマンスとコードの保守性の観点から、これにアプローチする最善の方法は何ですか?

4

1 に答える 1

2

PDFパーサーで作業しているときに同様の問題がありました。最初はiterateeパッケージを使用しました(ランダムアクセスをサポートしています)。私の知る限り、これはランダム IO をサポートする唯一の IO ライブラリです。

の現在のアプローチ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$
于 2013-02-24T15:09:12.527 に答える