1

サイズを見積もることができれば、初期容量で辞書を初期化するとパフォーマンスが向上する可能性があることを読みました。

Dim MyDataTable As New DataTable

'Fill MyDataTable from Database

Dim CapacityEstimate As integer = MyDataTable.Rows.Count
Dim MyDictionary As New Dictionary(Of String, MyObjectType)(CapacityEstimate)

'Fill the Dictionary independent of data table

CapacityEstimate 変数は、ディクショナリが保持する必要があるキーと値のペアの数の単なる見積もり (通常は 2500 から 7000 の範囲) です。したがって、それが 4000 であると見積もって 4010 個のオブジェクトになると (確かではありません)、辞書は大量のメモリを使用して、既に多くのキー/値のペアをサイズ変更します。これは良い解決策ですか、それとも初期容量で初期化しないほうがよいですか。ありがとう。

編集: 関連するが同じではない - .NET ジェネリック ディクショナリは、含まれるアイテムの数と同じ容量で初期化する必要がありますか?

4

4 に答える 4

4

それは良い質問です。私はそれを探し回っていませんでしたが、Odedの答えは私には良いようです。

ただし、概念的なマイクロベンチマークを実行してみましょう。

        Dictionary<string, int> FixedCapacity = new Dictionary<string, int>(4000);
        Dictionary<string, int> NotFixedCapacity = new Dictionary<string, int>();

        Stopwatch stopWatch = new Stopwatch();

        stopWatch.Start();

        for (int i = 0; i < 5000; i++)
        {
            FixedCapacity.Add(i.ToString(), i);
        }

        stopWatch.Stop();

        Console.WriteLine(string.Format("Fixed initial capacity: {0} ms", stopWatch.ElapsedMilliseconds));

        stopWatch.Reset();

        stopWatch.Start();

        for (int i = 0; i < 5000; i++)
        {
            NotFixedCapacity.Add(i.ToString(), i);
        }

        stopWatch.Stop();

        Console.WriteLine(string.Format("Not Fixed initial capacity: {0} ms", stopWatch.ElapsedMilliseconds));

        Console.ReadLine();

結果:

Fixed initial capacity: 1ms
Not Fixed initial capacity: 1ms

それは別の良い答えです、IMO =)

免責事項:いいえ、これは完全なベンチマーク手順ではありません。単一のマシンでフレームワークの「デフォルト」の動作を測定しているだけです。手動で数回実行したところ、ループに入っていなくても同じ結果が得られました。より優れたベンチマークツールをお持ちの場合は、テストして結果をここに貼り付けてください。

于 2012-08-10T19:20:55.930 に答える
3

小さなことは気にしないでください。そのような辞書は多くのメモリを使用しないため、サイズを変更しても多くのメモリを使用することはできません。実際のストレージはキーとデータのオブジェクトであり、ディクショナリにはそれらへの参照のみが含まれます。32 ビット モードではエントリあたり 8 バイトなので、4000 x 8 + オーバーヘッド = 32 キロバイトしかありません。

さらに、渡す容量は、ディクショナリ内のハッシュ バケットの数を計算するために使用されます。これは常に、指定した容量以上の素数です。素数はこの配列から選択されます (リファレンス ソースからコピーされます)。

    internal static readonly int[] primes = {
        3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
        1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
        17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
        187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
        1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};

したがって、4000 を渡すと、実際には 4049 個のバケットが得られます。これは、次に大きい素数です。したがって、4010 にオーバーシュートしても違いはありません。サイズを変更する必要がある場合は、容量が 2 倍になります。したがって、1 回のサイズ変更ですでに 8419 個のバケットが生成され、最大見積もりをはるかに超えています。サイズ変更もそれほど高価ではなく、数マイクロ秒です。そのため、アンドレには違いがわかりませんでした。

それについて推論する以外に、これは適切なアプローチです。測定。誰でも測定できます。

于 2012-08-10T20:12:40.913 に答える
1

ディクショナリは、すでに多くのキーと値のペアを使用してサイズを変更するために大量のメモリを使用しますか

辞書は、推定容量を超えた場合にのみ「サイズ変更」されます。

メモリは、推定したアイテム数のために予約されます - これはDictionary.

現在、容量と実際のサイズには違いがあります。容量は、内部ストレージのサイズを変更せずにディクショナリが保持できるアイテムの数です。サイズは、ディクショナリに実際に格納されている項目の数です (つまり、ディクショナリに追加された項目)。

于 2012-08-10T19:17:23.147 に答える
1

これが遅れているかもしれないことはわかっていますが、いずれにせよ、これを読む人にとってはまだ価値があるかもしれません. 容量を既知の値に設定する方がよい理由は、再割り当てを防ぐためです。24 時間年中無休の非常にビジーなサービス/アプリで、メモリ使用率が包括的/極端な状態になっている場合は、Capacity を既知のサイズ、または平均/推定サイズに設定してメモリの再割り当てを防止することで、余分な負荷がかからないようにする必要があります。

この場合のメモリの再割り当ては、メモリ空間に「(小さな)穴」を生成し、メモリの断片化につながる可能性があります。断片化が多すぎるためにメモリがまだ巨大であっても、アプリで「メモリ不足」の状態が発生する場合があります。

この観察は.Net 4.5.1まで真実でした。私が最後にこれをテスト/観察したときだと思います。新しいフレームワークのバージョンに適切な頻度でメモリ圧縮が行われるより優れたガベージ コレクタがあり、断片化の問題を軽減するか、これを小さなものにする場合、それはあまり問題になりません。

于 2016-01-04T18:15:25.233 に答える