[.net泛型学习笔记之二]泛型的性能
一般都说,泛型能够提高程序的性能,理由有:
l 避免装箱和拆箱操作。比方:
ArrayList list = new ArrayList();
list.Add(123); //需要装箱
int i = (int)list[0]; //需要拆箱
l 类型检查是在编译时间进行,而不是在运行时间进行的。比方:
ArrayList list = new ArrayList();
list.Add("I am a string.");
string i = (string)list[0]; //在运行时需要检查类型
本文主要对以上的理由进行验证,看看究竟是否真的如此,如果提高,究竟提高了多少。为此我们采用了以下方案测试:
测试方案 下载
样本
l 避免装箱和拆箱测试
1. int in Arraylist vs. int in List<T>
2. Large struct in ArrayList vs. Large class in ArrayList
3. Large struct in ArrayList vs. large struct in List<T>
其中Large Class和Large struct代码如下:
class LargeDataClass
{ decimal A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z;}
struct LargeDataStruct
{ decimal A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z;}
l 类型检查是在编译时间进行,而不是在运行时间进行的
1. custom class in ArrayList vs. custom class in List<T>
2. string in Arraylist vs. string in List<T> (本测试的目的,由于字符串是一个特殊的引用类型,而且经常使用,所以专门测试一下。)
custom class采用上面的LargeDataClass类。
这里的样本参见了:
http://blog.joycode.com/sunmast/archive/2005/12/16/csharp_generic_misleading.aspx
数据要求
每一个测试数据都测试3次,取平均值,并且剔除个别异常数据。
测试脚本
一般的测试脚本如下,在不同测试里,集合类型和元素的类型作相应修改就可以了。
public static double Test1()
{ GC.Collect();
ArrayList list1 = new ArrayList();
sw.Reset(); //sw是一个System.Diagnostics.Stopwatch对象
sw.Start();
for (int i = 0; i < Program.MaxCount; i++)
{ LargeDataStruct data1 = new LargeDataStruct();
list1.Add(data1); //测试装箱
LargeDataStruct data2 = (LargeDataStruct)list1[0]; //测试拆箱
}
sw.Stop();
return sw.Elapsed.TotalSeconds;
}
测试结果 下载
在P4 2.4GHz, 1G内存硬件,测试结果如下:
|
int in Arraylist |
int in List<T> |
large struct in ArrayList |
large class in ArrayList |
large struct in List<T> |
large class in List<T> |
string in ArrayList |
string in List<T> |
1000 |
0.0001134 |
0.000067 |
0.00148 |
0.000861 |
0.002366 |
0.000889 |
0.000459 |
0.000431 |
5000 |
0.0004195 |
0.0001952 |
0.006137 |
0.004077 |
0.016215 |
0.004753 |
0.002319 |
0.002125 |
10000 |
0.0007631 |
0.0002795 |
0.012613 |
0.008166 |
0.03255 |
0.008668 |
0.005103 |
0.005086 |
50000 |
0.0047294 |
0.0015961 |
0.136584 |
0.108376 |
0.137993 |
0.122591 |
0.024915 |
0.025379 |
100000 |
0.008883 |
0.0029012 |
0.337866 |
0.311848 |
0.278781 |
0.322081 |
0.050526 |
0.050124 |
500000 |
0.0541089 |
0.0169461 |
2.041508 |
1.989155 |
1.510633 |
2.014511 |
0.316258 |
0.314505 |
1000000 |
0.1205284 |
0.0333785 |
4.423767 |
4.147733 |
3.044961 |
4.120063 |
0.847025 |
0.837486 |
结果分析
l 避免装箱和拆箱测试
1. int in Arraylist vs. int in List<T>
List<T>比ArrayList快1-2倍。
2. Large struct in ArrayList vs. Large class in ArrayList
后者比前者快一些,提高幅度在10%-50%之间
3. Large struct in ArrayList vs. large struct in List<T>
在10000以下,前者的速度是后者的2-3倍。但当50000以上,两者的数据开始接近,当数据量>100000,后者的速度超过了前者,并且优势越来越大。产生这种情况的原因不明。
第1个、第2个的情况符合一般的预期。也符合书本上对.net泛型的宣传,但问题是,第3个为何情况如此奇怪?
l 类型检查是在编译时间进行,而不是在运行时间进行的
1. custom class in ArrayList vs. custom class in List<T> (custom class 即large class )
前者比后者速度稍快,在10%范围以内,故总体相当。
2. string in Arraylist vs. string in List<T>
后者比前者速度稍快,在10%范围以内,故总体相当。
这个结论和预期稍微有些出入,看来对于引用类型的对象,泛型并没有能够提高速度。
总结
对于泛型在的性能情况,似乎可以得出以下结论:
1) 对于int这样的简单值类型,泛型能够提高1-2倍的速度。
2) 对于复杂的值类型,在集合数据小于50000情况下,泛型的速度慢1-2倍。但在50000以上,泛型的速度将超过传统方式(但原因何在,是否是由其他原因造成,比如:垃圾收集等)。由于在实际的情况中,我们几乎不太可能把复杂的值类型放到集合中去(因为在这种情况下,修改值类型中的变量或属性不太方便,而且容易出错),所以这里的疑问或许也不用太去深究了。
3) 对于引用(Reference)类型,泛型和传统的方式速度相当。