0

何百万ものクラス Data のインスタンスがあり、最適化のアドバイスを求めています。

何らかの方法で最適化する方法はありますか-たとえば、何らかの方法でシリアル化することでメモリを節約しますが、重要な検索速度が低下します。クラスを構造体に変えるかもしれませんが、クラスは構造体に対してかなり大きいようです。

このオブジェクトのクエリは、一度に何億ものオブジェクトを取得できます。それらはリストにあり、DateTime によってクエリされます。結果はさまざまな方法で集計され、多くの計算を適用できます。

[Serializable]
[DataContract]
public abstract class BaseData {}

[Serializable]
public class Data : BaseData {
    public byte     member1;
    public int      member2;
    public long     member3;
    public double   member4;
    public DateTime member5;
}
4

2 に答える 2

2

残念ながら、「最適化」することを指定しましたが、取り組むべき正確な問題を指定していませんでした。したがって、一般的なアドバイス以上のことはできません。

シリアル化は役に立ちません。オブジェクトDataはすでにメモリにバイトとして格納されています。また、それを構造体の助けに変えることもありません。構造体とクラスの違いは、メモリ フットプリントではなく、アドレス指定と参照の動作にあります。

これらのオブジェクトが「何億も」あるコレクションのメモリ フットプリントを削減する唯一の方法は、全体をシリアル化して圧縮することです。しかし、それは実現不可能です。アクセスする前に常に全体を解凍する必要があります。これにより、パフォーマンスが大幅に低下し、実際にはアクセス時のメモリ消費量がほぼ 2 倍になります (圧縮データと解凍データの両方がその時点でメモリに存在します)。

私ができる最善の一般的なアドバイスは、このシナリオを自分で最適化しようとするのではなく、専用のソフトウェアを使用することです。特殊なソフトウェアとは、(インメモリ) データベースを意味します。インメモリで使用でき、必要なものがすべて .NET フレームワークに搭載されているデータベースの例として、SQLiteがあります。

于 2013-06-27T09:59:07.760 に答える
1

あなたが暗示しているように、多くのメンバーを持つクラスがあり、多数のインスタンスがあり、計算を実行するためにそれらすべてを同時にメモリに保持する必要があると思います。

説明したクラスに対して実際に異なるサイズを取得できるかどうかを確認するために、いくつかのテストを実行しました。

オブジェクトのメモリ内サイズを見つけるために、次の簡単な方法を使用しました。

private static void MeasureMemory()
{
    int size = 10000000;
    object[] array = new object[size];

    long before = GC.GetTotalMemory(true);
    for (int i = 0; i < size; i++)            
    {
        array[i] = new Data();
    }
    long after = GC.GetTotalMemory(true);

    double diff = after - before;

    Console.WriteLine("Total bytes: " + diff);
    Console.WriteLine("Bytes per object: " + diff / size);
}

原始的かもしれませんが、このような状況ではうまく機能することがわかりました。

予想どおり、そのクラスに対してできることはほとんどありません (構造体に変換する、継承を削除する、またはメソッド属性を削除する) ことは、単一のインスタンスによって使用されるメモリに影響を与えません。メモリ使用量に関する限り、それらはすべて同等です。ただし、実際のクラスをいじって、指定されたコードで実行してみてください。

インスタンスのメモリ フットプリントを実際に削減できる唯一の方法は、データを保持するためにより小さな構造を使用することです (たとえば、long ではなく int)。多数のブール値がある場合は、それらをバイトまたは整数にグループ化し、それらを操作する単純なプロパティ ラッパーを使用できます (ブール値は 1 バイトのメモリを必要とします)。これらはほとんどの状況では重要ではないかもしれませんが、1 億個のオブジェクトの場合、ブール値を削除すると 100 MB のメモリの違いが生じる可能性があります。また、アプリケーション用に選択したプラットフォーム ターゲットが、オブジェクトのメモリ フットプリントに影響を与える可能性があることに注意してください (x64 ビルドは、x86 ビルドよりも多くのメモリを消費します)。

データのシリアル化が役立つ可能性はほとんどありません。特に複雑なクエリを実行している場合、インメモリ データベースには利点があります。ただし、データのメモリ使用量を実際に削減できる可能性はほとんどありません。残念ながら、基本的なデータ型のフットプリントを減らす方法はあまりありません。ある時点で、ファイルベースのデータベースに移行する必要があります。

ただし、ここにいくつかのアイデアがあります。これらはハッキーで、高度に条件付きであり、計算パフォーマンスが低下し、コードの保守が難しくなることに注意してください。

  1. 大規模なデータ構造では、さまざまな状態のオブジェクトで一部のプロパティのみが入力され、他のプロパティが null または既定値に設定されることがよくあります。このようなプロパティのグループを特定できる場合は、おそらくそれらをサブクラスに移動し、複数のプロパティがスペースを占有する代わりに、null になる可能性のある参照を 1 つ持つことができます。次に、必要になったときにのみサブクラスをインスタンス化します。これを残りのコードから隠すことができるプロパティ ラッパーを作成できます。ここでの最悪のシナリオでは、すべてのプロパティをメモリに保持し、さらにいくつかのオブジェクト ヘッダーとポインターを保持することになります。

  2. おそらく、デフォルト値を取る可能性が高いメンバーをバイナリ表現に変換し、それらをバイト配列にパックすることができます。どのバイト位置がどのデータ メンバーを表しているかがわかり、それらを読み取ることができるプロパティを書き込むことができます。デフォルト値を持つ可能性が最も高いプロパティをバイト配列の末尾に配置します (たとえば、多くの場合 0 であるいくつかの long 型)。次に、オブジェクトを作成するときに、バイト配列のサイズを調整して、デフォルト値を持つプロパティをリストの最後から除外し、デフォルト値以外の値を持つ最初のメンバーに到達するまで続けます。外部コードがプロパティを要求した場合、バイト配列がそのプロパティを保持するのに十分な大きさであるかどうかを確認し、そうでない場合はデフォルト値を返すことができます。このようにして、スペースを節約できる可能性があります。最良の場合、いくつかのデータ メンバーの代わりに、バイト配列への null ポインターがあります。最悪の場合、完全なバイト配列が元のデータと同じくらいのスペースを占有し、さらに配列のオーバーヘッドが発生します。有用性は実際のデータに依存し、配列の再計算にはコストがかかるため、書き込みが比較的少ないことを前提としています。

これが役立つことを願っています:)

于 2013-06-27T13:00:30.350 に答える