代码改变世界

C# capacity property[翻译&&学习]

2011-09-07 20:35  一一九九  阅读(423)  评论(0编辑  收藏  举报

From:http://www.dotnetperls.com/capacity

虽然可以设置Collection的Capacity属性,你一定想知道为什么值得需要单独的设置Collection的Capacity属性把。这个选项用于调整分配的内存数量。这里我使用Dictinary和List来做个实验,测试一下他们的性能和影响。

测试结果:

class capacity time
Dictionary no capacity 1350ms
Dictionary has capacity 700ms
Dictionary const capacity 760ms
Dictionary over-large capacity 1005ms
list no 1050ms
list accurate 575ms
     

Capacity Property

当不指定Capacity的时候,Collection会被重新分配内存多次,直到长到100大小的时候。(注:原文应该是错误的,Colleciton的时间耗费主要耗费在了内存的重新分配,并将原有的内存区块重新复制到新的内存区块上了,分配的容量是之前的Double)

What Mircrosoft says:

"The capacity of a Dictionary(TKey, TValue) is the number of elements that can be added to the Dictionary(TKey, TValue) before resizing is necessary. As elements are added to a Dictionary(TKey, TValue), the capacity is automatically increased as required by reallocating the internal array."

So why set capacity?

假如能够评估出来Collection的大小,指定最初的容量大小能够在往Dictonary中添加新的元素的时候消除重新分配调整容量大小的操作。

Is it important ?

由于.net重新调正后台的数组的大小的成本较高,并且会加倍Collection的添加元素时间。

Memory  pressure

另外一个影响因素是当调整内存大小的时候造成的内存压力,这会导致GC回收的压力增大。

Benchmark capacity

下面是测试代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
class CapacityTest
{
    /// <summary>
    /// The number of iterations.
    /// </summary>
    static int _m = 100000;
 
    /// <summary>
    /// List of values we use for the test.
    /// </summary>
    List<string> _values = new List<string>();
 
    public CapacityTest()
    {
    // 100 random strings for benchmarking.
    for (int i = 0; i < 100; i++)
    {
        _values.Add(System.IO.Path.GetRandomFileName());
    }
 
    long t1 = Environment.TickCount;
 
    A_Dictionary();
 
    long t2 = Environment.TickCount;
 
    B_DictionaryCapacity();
 
    long t3 = Environment.TickCount;
 
    C_DictionaryCapacity();
 
    long t4 = Environment.TickCount;
 
    D_DictionaryCapacity();
 
    long t5 = Environment.TickCount;
 
    E_List();
 
    long t6 = Environment.TickCount;
 
    F_ListCapacity();
 
    long t7 = Environment.TickCount;
 
    // Write dictionary times
    Console.WriteLine(t2 - t1);
    Console.WriteLine(t3 - t2);
    Console.WriteLine(t4 - t3);
    Console.WriteLine(t5 - t4);
 
    // Write list times
    Console.WriteLine(t6 - t5);
    Console.WriteLine(t7 - t6);
    Console.ReadLine();
    }
 
    void A_Dictionary()
    {
    // No capacity
    for (int i = 0; i < _m; i++)
    {
        Dictionary<string, int> d = new Dictionary<string, int>();
        foreach (string k in _values)
        {
        d.Add(k, 0);
        }
    }
    }
 
    void B_DictionaryCapacity()
    {
    // Capacity from collection Count
    for (int i = 0; i < _m; i++)
    {
        Dictionary<string, int> d = new Dictionary<string, int>(_values.Count);
        foreach (string k in _values)
        {
        d.Add(k, 0);
        }
    }
    }
 
    void C_DictionaryCapacity()
    {
    // Const capacity
    for (int i = 0; i < _m; i++)
    {
        Dictionary<string, int> d = new Dictionary<string, int>(100);
        foreach (string k in _values)
        {
        d.Add(k, 0);
        }
    }
    }
 
    void D_DictionaryCapacity()
    {
    // Huge capacity (1 order of magnitude too large)
    for (int i = 0; i < _m; i++)
    {
        Dictionary<string, int> d = new Dictionary<string, int>(1000);
        foreach (string k in _values)
        {
        d.Add(k, 0);
        }
    }
    }
 
    void E_List()
    {
    // No capacity
    for (int i = 0; i < _m * 5; i++)
    {
        List<string> l = new List<string>();
        foreach (string k in _values)
        {
        l.Add(k);
        }
    }
    }
 
    void F_ListCapacity()
    {
    // Exact capacity
    for (int i = 0; i < _m * 5; i++)
    {
        List<string> l = new List<string>(100);
        foreach (string k in _values)
        {
        l.Add(k);
        }
    }
    }
}

Predict capacities

这里作者没有给出比较好的法子,大体上还是通过预估的方式进行。