WindowsXP 和 WindowsCE 中的 .NET 集合性能测试
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
的派生类。M
yTestFactory.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()]
在结果中,我们将发现以下论点
- 工厂模式需要更高的成本。
- 泛型集合比普通集合更快。
- 但在 XP 中,有时
List
比ArrayList
性能更差。
参考
/// <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 新文章