65.9K
CodeProject 正在变化。 阅读更多。
Home

WindowsXP 和 WindowsCE 中的 .NET 集合性能测试

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.40/5 (4投票s)

2011 年 7 月 26 日

CPOL

2分钟阅读

viewsIcon

24132

downloadIcon

146

WindowsXP 和 WindowsCE 中的 .NET 集合性能测试

引言

在本文中,我们将测试集合的性能。在我们的项目中,执行器将由 ExecuterFactory 创建,并且我们有超过 18 种派生类。在运行时创建执行器会产生高昂的成本,因此我们需要一种解决方案来提高性能。

最佳解决方案是 对象池模式 或缓存模式。这两种模式的区别在于对象状态是否需要重置。对象池模式的定义需要在从池中获取对象时重置状态(例如成员值),而缓存模式始终保持状态。

我将开发一个索引缓存。

背景

我将尝试使用最适合我的需求的 .NET 集合。首先,队列 (FIFO) 和堆栈 (FILO) 不能满足我的需求,我需要在池或缓存中保留对象,并使用键来获取对象。最后,我将尝试 List(ArrayList)Dictionary(Hashtable)SortedList
我将尝试 List,因为我的集合不会包含太多的元素,并且在这种情况下,我知道键,所以我认为 List(ArrayList) 是最好的。
但我需要一份测试报告来说服同事。

我所做的事情

首先,我尝试测试使用工厂模式创建对象会损失多少成本。

static void TestNew()
{
    for (int i = 0; i < max; i++)
    {
        MyTest orz1 = new MyTest01();
        orz1.Id = i;
        MyTest orz2 = new MyTest02();
        orz2.Id = i;
        MyTest orz3 = new MyTest03();
        orz3.Id = i;
    }
}

static void TestFactory()
{
    for (int i = 0; i < max; i++)
    {
        int type = ((i % maxclass) + 1);
        MyTest orz1 = MyTestFactory.Create(type);
        orz1.Id = i;
        MyTest orz2 = MyTestFactory.Create(type);
        orz2.Id = i;
        MyTest orz3 = MyTestFactory.Create(type);
        orz3.Id = i;
    }
}		

MyTest 类是一个简单的类,而 MyClass01~20 是 MyClass 的派生类。
MyTestFactory.Create 是一个工厂方法,用于创建 MyClass01~20。

我将所有类添加到集合中,然后从集合中获取类。

static void TestDictionary()
{
    Dictionary<int, MyTest> table = new Dictionary<int, MyTest>();
    for (int i = 1; i < maxclass + 1; i++)
    {
        table.Add(i, MyTestFactory.Create(i));
    }
    for (int i = 0; i < max; i++)
    {
        MyTest orz = table[(i % maxclass) + 1];
        orz.Id = i;
        MyTest orz2 = table[(i % maxclass) + 1];
        orz2.Id = i;
        MyTest orz3 = table[(i % maxclass) + 1];
        orz3.Id = i;
    }
}

static void TestHashTable()
{
    Hashtable table = new Hashtable();
    for (int i = 1; i < maxclass + 1; i++)
    {
        table.Add(i, MyTestFactory.Create(i));
    }
    for (int i = 0; i < max; i++)
    {
        MyTest orz = table[(i % maxclass) + 1] as MyTest;
        orz.Id = i;
        MyTest orz2 = table[(i % maxclass) + 1] as MyTest;
        orz2.Id = i;
        MyTest orz3 = table[(i % maxclass) + 1] as MyTest;
        orz3.Id = i;
    }
}

static void TestArrayList()
{
    ArrayList list = new ArrayList();
    for (int i = 1; i < maxclass + 1; i++)
    {
        list.Add(MyTestFactory.Create(i));
    }
    for (int i = 0; i < max; i++)
    {
        MyTest orz = list[(i % maxclass)] as MyTest;
        orz.Id = i;
        MyTest orz2 = list[(i % maxclass)] as MyTest;
        orz2.Id = i;
        MyTest orz3 = list[(i % maxclass)] as MyTest;
        orz3.Id = i;
    }
}

static void TestList()
{
    List<MyTest> list = new List<MyTest>();
    for (int i = 1; i < maxclass + 1; i++)
    {
        list.Add(MyTestFactory.Create(i));
    }
    for (int i = 0; i < max; i++)
    {
        MyTest orz = list[(i % maxclass)];
        orz.Id = i;
        MyTest orz2 = list[(i % maxclass)];
        orz2.Id = i;
        MyTest orz3 = list[(i % maxclass)];
        orz3.Id = i;
    }
}

static void TestSortedList()
{
    SortedList<int, MyTest> slist = new SortedList<int, MyTest>();
    for (int i = 1; i < maxclass + 1; i++)
    {
        slist.Add(i, MyTestFactory.Create(i));
    }
    for (int i = 0; i < max; i++)
    {
        MyTest orz = slist[(i % maxclass) + 1];
        orz.Id = i;
        MyTest orz2 = slist[(i % maxclass) + 1];
        orz2.Id = i;
        MyTest orz3 = slist[(i % maxclass) + 1];
        orz3.Id = i;
    }
}

测试结果

最后,我在 WindowsXP、WindowsCE 5.0 模拟器和我们的 WindowsCE 6.0 设备上测试了这段代码。

=======Test in WindowsXP========
Class number: 20
Performance test with run 1000000time
Elapsed = 00:00:00.0727499 in [Void TestNew()]
Elapsed = 00:00:00.2222732 in [Void TestFactory()]
Elapsed = 00:00:00.0458594 in [Void TestArrayList()]
Elapsed = 00:00:00.0467893 in [Void TestList()]
Elapsed = 00:00:00.2287626 in [Void TestSortedList()]
Elapsed = 00:00:00.1790526 in [Void TestHashTable()]
Elapsed = 00:00:00.1090860 in [Void TestDictionary()]

=======Test in WindowsXP========
Class number: 20
Performance test with run 1000000time
Elapsed = 00:00:00.1094648 in [Void TestNew()]
Elapsed = 00:00:00.2111686 in [Void TestFactory()]
Elapsed = 00:00:00.0535788 in [Void TestArrayList()]
Elapsed = 00:00:00.0486265 in [Void TestList()]
Elapsed = 00:00:00.2336329 in [Void TestSortedList()]
Elapsed = 00:00:00.1785616 in [Void TestHashTable()]
Elapsed = 00:00:00.1092564 in [Void TestDictionary()]

=======Test in WindowsCE 6.0 Device========
Class number: 20
Performance test with run 100000time
Elapsed = 00:00:00.3068472 in [Void TestNew()]
Elapsed = 00:00:00.6614232 in [Void TestFactory()]
Elapsed = 00:00:00.2436380 in [Void TestArrayList()]
Elapsed = 00:00:00.1280703 in [Void TestList()]
Elapsed = 00:00:00.8922895 in [Void TestSortedList()]
Elapsed = 00:00:01.3348975 in [Void TestHashTable()]
Elapsed = 00:00:00.9218634 in [Void TestDictionary()]

=======Test in WindowsCE 5.0 Emulator========
Class number: 20
Performance test with run 100000time
Elapsed = 00:00:00.2321512 in [Void TestNew()]
Elapsed = 00:00:00.7489057 in [Void TestFactory()]
Elapsed = 00:00:00.2709390 in [Void TestArrayList()]
Elapsed = 00:00:00.1322352 in [Void TestList()]
Elapsed = 00:00:00.7034211 in [Void TestSortedList()]
Elapsed = 00:00:01.2571927 in [Void TestHashTable()]
Elapsed = 00:00:00.7799420 in [Void TestDictionary()]

在结果中,我们将发现以下论点

  1. 工厂模式需要更高的成本。
  2. 泛型集合比普通集合更快。
  3. 但在 XP 中,有时 ListArrayList 性能更差。

参考

/// <summary>
/// Performance Counter to calculate function executing time.
/// </summary>
public class Performance
{
    public delegate void CalculateHanlder();

    /// <summary>
    /// Using Performance.CalculFunction(MethodName)
    /// It will print executing time in Console.
    /// </summary>
    /// <param name="method">A none argument method</param>
    public static void CalculateMethod(CalculateHanlder method)
    {
        Stopwatch st = new Stopwatch();
        st.Start();
        method.Invoke();
        st.Stop();
        string msg = string.Format("Elapsed = {0} in [{1}]", 
			st.Elapsed.ToString(), method.Method.ToString());
        Console.WriteLine(msg);
        Debug.WriteLine(msg);
    }
}

Performance.CalculateMethod(TestNew); 

我使用这段代码来计算方法的执行时间。CalculateHanlder 是一个委托,将一个无参数方法传递给它以计算时间。

索引缓存

public interface IIndexCache
{
    void Register(object obj);
    void Unregister(object obj);
    object New(int index);
    void Delete(int index);
}

public interface IIndexCache<T>
{
    void Register(T obj);
    void Unregister(T obj);
    T New(int index);
    void Delete(int index);
} 

首先,声明一个普通接口和一个泛型接口,名为 IIndexCache

  • Register:实现此函数将对象注册到缓存中。
  • Unregister:实现此函数将对象从缓存中注销。
  • New:实现此函数从缓存中获取对象。
  • Delete:实现此函数将对象放回缓存中。
public abstract class _IndexCache
{
    protected enum ObjectState
    {
        NO_USE,
        USING
    }

    protected List<ObjectState> m_objState = new List<ObjectState>();

    protected _IndexCache()
    {
    }
} 

我希望缓存中的对象不需要实现接口或继承类,所以我创建了一个 abstract _IndexCache

我声明了一个 enum ObjectState,并使用它来保持对象的状态。

public class IndexCache :_IndexCache,IIndexCache
{
    /// <summary>
    /// Using for pool
    /// </summary>
    ArrayList m_Pool;

     /// <summary>
    /// Not use, set private to disable new.
    /// </summary>
    public IndexCache() : base()
    {
        m_Pool = new ArrayList();
    }

    #region IIndexCache Members

    /// <summary>
    /// Use IndexCache<X>.New(index) to get a instance.
    /// if the pool is empty or object is not using will return null.
    /// </summary>
    /// <param name="index">index in cache</param>
    /// <returns>T is a type of class</returns>
    public object New(int index)
    {
        object obj = null;
        if (index < m_Pool.Count && m_objState[index] == ObjectState.NO_USE)
        {
            obj = m_Pool[index];
            m_objState[index] = ObjectState.USING;
        }
        else if (m_objState[index] == ObjectState.USING)
        {
            throw new Exception("[" + index + "] was be used.");
        }
        else if (index >= m_Pool.Count)
        {
            throw new Exception("[" + index + "] not exist in cache.");
        }
        return obj;
    }

    /// <summary>
    /// Put back into cache
    /// </summary>
    /// <param name="index">object index in cache</param>
    public void Delete(int index)
    {
        if (index < m_Pool.Count)
        {
            m_objState[index] = ObjectState.NO_USE;
        }
        else
        {
            throw new Exception("[" + index + "] not exist in cache.");
        }
    }

    /// <summary>
    /// Register an object in cache
    /// </summary>
    /// <param name="obj">An object to register</param>
    public void Register(object obj)
    {
        m_Pool.Add(obj);
        m_objState.Add(ObjectState.NO_USE);
    }

    /// <summary>
    /// Unregister an object.
    /// </summary>
    /// <param name="obj">An object to unregister</param>
    public void Unregister(object obj)
    {
        int index = m_Pool.IndexOf(obj);
        m_Pool.Remove(obj);
        m_objState.RemoveAt(index);
    }

    #endregion

    /// <summary>
    /// Get the information for this cache.
    /// </summary>
    /// <returns>information</returns>
    public new string ToString()
    {
        return typeof(object).ToString() + 
		" IndexCache has [" + m_Pool.Count + "] items.";
    }
}
public class TIndexCache<T> : _IndexCache, IIndexCache<T>
{
    /// <summary>
    /// Using for pool
    /// </summary>
    List<T> m_Pool;

    /// <summary>
    /// Not use, set private to disable new.
    /// </summary>
    public TIndexCache()
        : base()
    {
        m_Pool = new List<T>();
    }

    /// <summary>
    /// Use IndexCache<X>.New(index) to get a instance.
    /// if the pool is empty or object is not using will return null.
    /// </summary>
    /// <param name="index">index in cache</param>
    /// <returns>T is a type of class</returns>
    public T New(int index)
    {
        T obj = default(T);
        if (index < m_Pool.Count && m_objState[index] == ObjectState.NO_USE)
        {
            obj = m_Pool[index];
            m_objState[index] = ObjectState.USING;
        }
        else if (m_objState[index] == ObjectState.USING)
        {
            throw new Exception("[" + index + "] was be used.");
        }
        else if (index >= m_Pool.Count)
        {
            throw new Exception("[" + index + "] not exist in cache.");
        }
        return obj;
    }

    /// <summary>
    /// Put back into cache
    /// </summary>
    /// <param name="index">object index in cache</param>
    public void Delete(int index)
    {
        if (index < m_Pool.Count)
        {
            m_objState[index] = ObjectState.NO_USE;
        }
        else
        {
            throw new Exception("[" + index + "] not exist in cache.");
        }
    }

    /// <summary>
    /// Register an object in cache
    /// </summary>
    /// <param name="obj">An object to register</param>
    public void Register(T obj)
    {
        m_Pool.Add(obj);
        m_objState.Add(ObjectState.NO_USE);
    }

    /// <summary>
    /// Unregister an object.
    /// </summary>
    /// <param name="obj">An object to unregister</param>
    public void Unregister(T obj)
    {
        int index = m_Pool.IndexOf(obj);
        m_Pool.Remove(obj);
        m_objState.RemoveAt(index);
    }

    /// <summary>
    /// Get the information for this cache.
    /// </summary>
    /// <returns>information</returns>
    public new string ToString()
    {
        return typeof(T).ToString() + " IndexCache has [" + m_Pool.Count + "] items.";
    }
} 
  • Register:调用此函数,它会将对象添加到缓存中,并为该对象添加状态
  • UnRegister:调用此函数,它将从缓存中删除对象,并删除该对象的状态
  • New:调用此函数,它将返回一个对象并更改对象状态
  • Delete:调用此函数,它会将对象状态设置为 NO_USE

历史

  • v1.1 添加 IndexCache
  • v1.0 新文章
WindowsXP 和 WindowsCE 中的 .NET 集合性能测试 - CodeProject - 代码之家
© . All rights reserved.