7

現在、私はあるタスクを実行するために何GBものデータをクライアントマシンに持ち込む必要があるプロジェクトに取り組んでおり、タスクにはデータの分析を行い、意思決定プロセスに役立つため、データ全体が必要です。

問題は、クライアント マシンとアプリケーションのパフォーマンスを損なうことなく大量のデータをメモリに管理するためのベスト プラクティスと適切なアプローチは何かということです。

注: アプリケーションのロード時に、データベースからクライアント マシンにデータを移動するのに時間を費やすことができますが、これは私たちの場合はまったく問題ありません。しかし、起動時にデータがアプリケーションにロードされると、パフォーマンスが非常に重要になります。

4

1 に答える 1

15

これは、現在直面している問題など、問題の説明がないと答えるのが少し難しいですが、以下は、同様のシナリオでの最近の経験に基づいたいくつかの考えです. ただし、このタイプのモデルに変更するのは大変な作業です。そのため、それを「修正」するためにどれだけ投資できるかにもよります。「あなたの問題」が「私たちの問題」と同じであるとは約束できません。問題」、私の言いたいことがわかるなら。したがって、次のアプローチがうまくいかない場合でも、クロスしないでください。


それだけの量のデータをメモリにロードすると、常に何らかの影響が生じますが、あなたが何をしているのかがわかると思います...

それだけの量のデータを単純にロードすると、多数 (数百万?) のオブジェクトと、同様またはそれ以上の数の参照が作成されます。明らかに x64 を使用したいので、参照が増えますが、パフォーマンスの面で最大の問題はガベージ コレクションです。収集できないオブジェクトがたくさんありますが、GC は大量のメモリを使用していることを認識し、とにかく定期的に試行します。これは、こちらで詳しく調べたものですが、次のグラフはその影響を示しています。特に、これらの「スパイク」はすべて GC キリング パフォーマンスです。

http://marcgravell.blogspot.co.uk/2011/10/assault-by-gc.html

このシナリオ (膨大な量のデータがロードされ、決して解放されない) では、structs を使用するように切り替えました。つまり、データを次の場所にロードします。

struct Foo {
    private readonly int id;
    private readonly double value;
    public Foo(int id, double value) {
        this.id = id;
        this.value = value;
    }
    public int Id {get{return id;}}
    public double Value {get{return value;}}
}

それらを配列(リストではなく)に直接保存しました:

Foo[] foos = ...

これの重要な点は、これらの構造体のいくつかは非常に大きいため、スタック上で何度も自分自身をコピーしたくないということですが、配列を使用すると次のことができます。

private void SomeMethod(ref Foo foo) {
     if(foo.Value == ...) {blah blah blah}
}
// call ^^^
int index = 17;
SomeMethod(ref foos[index]);

オブジェクトを直接渡したことに注意してください- オブジェクトは決してコピーされませんでした。foo.Value実際には配列の内部を直接見ています。オブジェクト間のリレーションシップが必要な場合は、トリッキーなビットが始まります。これは であるため、ここに参照を保存することはできません。また、それを保存structすることもできません。ただし、できることは、インデックスを (配列に) 格納することです。例えば:

struct Customer {
      ... more not shown
      public int FooIndex { get { return fooIndex; } }
}

ほど便利ではありませんcustomer.Fooが、次のようにするとうまくいきます。

Foo foo = foos[customer.FooIndex];
// or, when passing to a method, SomeMethod(ref foos[customer.FooIndex]);

キーポイント:

  • 現在、「参照」には半分のサイズを使用しています (anintは 4 バイト、x64 の参照は 8 バイト)。
  • メモリ内に数百万のオブジェクト ヘッダーがない
  • GC が参照する巨大なオブジェクト グラフはありません。GC が信じられないほど迅速に調べることができる少数の配列のみ
  • ただし、操作が少し不便で、ロード時に初期処理が必要です

その他の注意事項:

  • 文字列はキラーです。何百万もの文字列がある場合、それは問題です。少なくとも、繰り返される文字列がある場合string.Internは、同じ内容の 800,000 個の文字列ではなく、繰り返される各値のインスタンスが 1 つだけになるように、カスタムインターンを実行してください (悪いことではありません)。
  • サブリスト/配列ではなく、有限長の繰り返しデータがある場合は、fixed配列を検討できます。これにはunsafeコードが必要ですが、別の無数のオブジェクトと参照を回避します

追加の脚注として、その量のデータでは、シリアル化プロトコル、つまりデータをどのように送信するかについて真剣に考える必要があります。XmlSerializerDataContractSerializerまたはのようなものから遠く離れることを強くお勧めしBinaryFormatterます。この件に関する指針が必要な場合は、お知らせください。

于 2012-09-13T07:21:44.560 に答える