21

ベンチマークは、cerealライブラリが私のデータ構造(以下に詳述)を逆シリアル化するのに、ドライブから同じデータを読み取るのにかかる時間よりも100倍長い時間がかかることを示しています。

benchmarking Read
mean: 465.7050 us, lb 460.9873 us, ub 471.0938 us, ci 0.950
std dev: 25.79706 us, lb 22.19820 us, ub 30.81870 us, ci 0.950
found 4 outliers among 100 samples (4.0%)
  4 (4.0%) high mild
variance introduced by outliers: 53.460%
variance is severely inflated by outliers

benchmarking Read + Decode
collecting 100 samples, 1 iterations each, in estimated 6.356502 s
mean: 68.85135 ms, lb 67.65992 ms, ub 70.05832 ms, ci 0.950
std dev: 6.134430 ms, lb 5.607914 ms, ub 6.755639 ms, ci 0.950
variance introduced by outliers: 74.863%
variance is severely inflated by outliers

これは、私のプログラムでこのデータ構造の一般的な逆シリアル化の使用法をプロファイリングすることによってもサポートされます。このプログラムでは、時間の98%がデータの逆シリアル化に費やされ、1%がIOコアアルゴリズムに加えられます。

COST CENTRE                    MODULE               %time %alloc

getWord8                       Data.Serialize.Get    30.5   40.4
unGet                          Data.Serialize.Get    29.5   17.9
getWord64be                    Data.Serialize.Get    14.0   10.7
getListOf                      Data.Serialize.Get    10.2   12.8
roll                           Data.Serialize         8.2   11.5
shiftl_w64                     Data.Serialize.Get     3.4    2.9
decode                         Data.Serialize         2.9    3.1
main                           Main                   1.3    0.6

私が逆シリアル化するデータ構造はIntMap [Triplet Atom]であり、コンポーネントタイプの定義を以下に示します。

type Triplet a = (a, a, a)

data Point = Point {
    _x :: {-# UNPACK #-} !Double ,
    _y :: {-# UNPACK #-} !Double ,
    _z :: {-# UNPACK #-} !Double }

data Atom = Atom {
    _serial :: {-# UNPACK #-} !Int    ,
    _r      :: {-# UNPACK #-} !Point  ,
    _n      :: {-# UNPACK #-} !Word64 }

デフォルトIntMapの、によって提供されるインスタンス、(,,)およびカスタムタイプに次のタイプとインスタンスを使用しています。[]cereal

instance Serialize Point where
    put (Point x y z) = do
        put x
        put y
        put z
    get = Point <$> get <*> get <*> get

instance Serialize Atom where
    put (Atom s r n) = do
        put s
        put r
        put n
    get = Atom <$> get <*> get <*> get

だから私の質問は:

  1. 一般的に、デシリアライズが非常に遅いのはなぜですか?
  2. デシリアライズを高速化するためにデータ構造(つまりIntMap/ )を変更する方法はありますか?[]
  3. デシリアライズを高速化するためにデータ型(つまりAtom/ )を変更する方法はありますか?Point
  4. Haskell内よりも高速な代替手段はありcerealますか、それともより迅速な逆シリアル化(つまり使用mmap)を行うためにデータ構造をC-landに格納する必要がありますか?

私が逆シリアル化するこれらのファイルは、完全なインデックスがターゲットコンピューター(コンシューマーグレードのデスクトップ)のメモリに収まらないため、検索エンジンのサブインデックスに使用されています。そのため、各サブインデックスをディスクに保存して読み取ります+メモリ内にある最初のグローバルインデックスが指すサブインデックスをデコードします。cerealまた、インデックスの検索はエンドユーザーのボトルネックであり、現在のシリアル化パフォーマンスはインデックスの生成と更新に十分であるため、シリアル化の速度については心配していません。

編集:

スペース効率の良いトリプレットを使用するというドンの提案を試してみたところ、速度が4倍になりました。

benchmarking Read
mean: 468.9671 us, lb 464.2564 us, ub 473.8867 us, ci 0.950
std dev: 24.67863 us, lb 21.71392 us, ub 28.39479 us, ci 0.950
found 2 outliers among 100 samples (2.0%)
  2 (2.0%) high mild
variance introduced by outliers: 50.474%
variance is severely inflated by outliers

benchmarking Read + Decode
mean: 15.04670 ms, lb 14.99097 ms, ub 15.10520 ms, ci 0.950
std dev: 292.7815 us, lb 278.8742 us, ub 308.1960 us, ci 0.950
variance introduced by outliers: 12.303%
variance is moderately inflated by outliers

ただし、それでもIOの25倍の時間を消費するボトルネックのままです。また、ドンの提案が機能する理由を誰かが説明できますか?これは、リスト以外のもの(配列など)に切り替えた場合にも改善される可能性があることを意味しますか?

編集#2:最新のHaskellプラットフォームに切り替えて、シリアルのプロファイリングを再実行しました。情報はかなり詳細であり、私はそれのhpasteを提供しました。

4

1 に答える 1

9

Ok。アドバイスの要約でこれに答える。データの高速デシリアライズの場合:

  • cereal(厳密なバイト文字列出力)またはbinary(遅延バイト文字列出力)を使用します
  • これらのライブラリはオーバーヘッドを削除するためにインライン化に依存しているため、-O2を使用してコンパイルしていることを確認してください
  • ポリモーフィックタプルを解凍された特殊なフォームに置き換えるなど、高密度のデータ型を使用します。
  • データ型をリストに変換してシリアル化することは避けてください。バイトストリングがある場合、これは処理されます。解凍された配列タイプの場合、通常は非常に高速なIOが得られますが、インスタンスを再確認する価値があります
  • mmapされたIOを使用できる場合があります
  • ダブルヘビーデータの場合は、より効率的なダブルリーダーを検討してください。
  • 最新のGHCバージョンで、パフォーマンスに合わせて調整された最新のアレイおよびコンテナタイプを使用します。
于 2012-06-07T12:00:02.893 に答える