2012-08-10 21 views
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個對象(我可能會超出或低於,不確定),字典是否會使用大量內存來調整其中已有的許多鍵/值對。這是一個好的解決方案,還是我最好不用初始容量進行初始化。謝謝。

編輯:相關但不相同 - Should a .NET generic dictionary be initialised with a capacity equal to the number of items it will contain?

回答

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是不會有所作爲的。如果它確實需要調整大小,那麼它的容量就會翻倍。所以一個調整大小已經產生了8419個桶,遠遠超過了你的最大估計。調整大小也不是很昂貴,幾微秒。這就是爲什麼安德烈看不出有什麼不同。

除了對此的推理之外,哪一種是正確的方法。測量。任何人都可以測量。

1

將在字典中它

字典使用大量的內存與多鍵/值對調整已只會「調整大小「如果你去超過的容量估計。

內存將被保留爲您估計的項目數 - 這將發生在Dictionary的構造函數中。

現在,容量和實際大小之間存在差異。容量是字典可以在沒有調整內部存儲的情況下保存多少項。大小是存儲在字典中的項目實際上的數量(即添加到它的項目)。

+0

我在哪裏暗示實際尺寸和容量是相同的 – swiftgp 2012-08-10 19:21:08

+0

@ user1556110 - 我的答案並非只針對您自己 - 而是針對讀取問題的每個人。 – Oded 2012-08-10 19:22:26

3

這是一個很好的問題。我沒有搜索過它,但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 =)

免責聲明:沒有,這不是一個完整的基準程序,我只是在一臺機器上測量框架的「默認」行爲。我已經多次手動運行它,並得到相同的結果,即使它不在循環中。如果您有更好的基準測試工具,請對其進行測試並在此處粘貼結果。

+0

@pst我相信一個概念性的步驟就夠了。但是,你會建議什麼? – 2012-08-10 19:25:29

+0

@pst你走了,更新了答案=) – 2012-08-10 19:28:57

-1

我知道這可能比較晚,但無論如何,這對任何閱讀此內容的人都是有價值的。將容量設置爲某個已知值的原因是爲了防止重新分配。在高度繁忙的24x7服務/應用程序中,內存利用率是全面/極端的情況,您可能希望避免通過將容量設置爲已知大小或某些平均/估計大小來防止內存重新分配來增加額外壓力。

在這種情況下,內存重新分配會在內存空間中生成「(小)空洞」,從而導致內存碎片。即使內存仍然很龐大,但由於碎片過多,您的應用程序可能會遇到「內存不足」的情況。

這個觀察是真實的.Net 4.5.1我相信是當我最後測試/觀察到這一點。如果較新的框架版本具有更好的垃圾收集器,這種垃圾收集器可以在正常的頻率下進行內存壓縮,因此可以減輕碎片問題或使其成爲次要事情,那麼它並不重要。