2011-08-11 8 views
4

(私が持っている)カスタムエンコード関数を使用して、大きな値リストをシリアライズする必要があります。私はこれを行って、それが動作しますが、私はまた、どれだけ多くの値がシリアル化され、ディスクに書き込まれている間、比較的一定量のメモリを使用しているのかをカウントしたいと思います(つまり、それは非常に大)を取得するように、周りのリスト。値リストのシリアライズとカウント

カウントを保持する必要がなく、バイナリ、シリアル、ブレーズ作成者がすべて機能します(B.writeFile "foo" . runPut . mapM_ encodeValue相当)。しかし、私がこれらのライブラリを使って何をしようとしても、結果のByteStringは、チャンクが利用可能になるとすぐにディスクに書き込まれるのではなく、完了するまでメモリ内に保持されているようです(toByteStringIOをblaze -ビルダー)。

これは私がやろうとしてきたものを証明する最小限の例です。

import Data.Binary 
import Data.Binary.Put 
import Control.Monad(foldM) 
import qualified Data.ByteString.Lazy as B 

main :: IO() 
main = do let ns = [1..10000000] :: [Int] 
       (count,b) = runPutM $ foldM (\ c n -> c `seq` (put n >> return (c+1))) (0 :: Int) ns 
      B.writeFile "testOut" b 
      print count 

コンパイルと+RTS -hyで実行すると、結果は値をバイト文字列によって支配され、ほぼ三角形のグラフです。

私が今までに見つけた唯一の解決策(私が大ファンではない)は、Putまたは直接ビルドの値よりもむしろB.appendFileを使用してIO内のループ(直接またはfoldM)を行うことです私にはそれほどエレガントではないようです。より良い方法がありますか?

答えて

2

toByteStringIOはうまくいきません。うまくいけば、そのライブラリをよく知っている人が答えを出してくれるはずです。

これは、ストリーム処理をIOアクションと混在させたいときはいつも、イテレートが最も洗練された解決策であることがわかります。これは、処理され保持されるデータの量とストリーミングの側面を他の任意のIOアクションと結合するための正確な制御を可能にするためです。ハッカーにはseveraliterateeimplementationsがあります。この例は、「iteratee」と一緒です。なぜなら、それは私が最もよく知っているものですから。

import Data.Binary.Put 
import Control.Monad 
import Control.Monad.IO.Class 
import qualified Data.ByteString.Lazy as B 
import Data.ByteString.Lazy.Internal (defaultChunkSize) 
import Data.Iteratee hiding (foldM) 
import qualified Data.Iteratee as I 

main :: IO() 
main = do 
    let ns = [1..80000000] :: [Int] 
    iter <- enumPureNChunk ns (defaultChunkSize `div` 8) 
          (joinI $ serializer $ writer "testOut") 
    count <- run iter 
    print count 

serializer = mapChunks ((:[]) . runPutM . foldM 
    (\ !cnt n -> put n >> return (cnt+1)) 0) 

writer fp = I.foldM 
    (\ !cnt (len,ck) -> liftIO (B.appendFile fp ck) >> return (cnt+len)) 
    0 

これには3つの部分があります。 writerは、「iteratee」、すなわちデータ消費者である。データの各チャンクを受信したものとして書き込み、長さの実行カウントを保持します。 serializerは、ストリームトランスフォーマーa.k.a. "enumeratee"です。それはタイプ[Int]の入力チャンクを取り、タイプ[(Int, B.ByteString)](バイト数、バイト数)のストリームにそれを直列化します。最後にenumPureNChunkは "列挙子"であり、ストリームを生成します。この場合、入力リストから生成されます。入力から、遅延レイアウトチャンクを1つだけ満たすために十分な要素が必要です(32ビットシステムの場合は4で割って4で割っています)。そしてそれらをGCにするためにディスクに書き込みます。

+0

なぜ他の値ではなく8192ですか?そして、iterateeで見つけられなかったので、どこで 'run'関数の定義と型を見つけることができますか? – ivanm

+0

私はあなたのソリューションが私自身の解決策を持っていた問題を実証していると思いますが、ByteStringを構築して、同時にいくつの値があるのか​​を数えることはできないようです。あなたはディスクに書き込む間にそれをしなければならないようです。 – ivanm

+0

ああ、あなたがしていることは、私の元のバージョンとは異なる面があります。あなたのコードを理解すれば、番号ではなくディスクに保存された**バイト数**を返して印刷しています。 **値**のしたがって、それは間違っています:( – ivanm

関連する問題