接上篇,发现一个神奇的现象,List<object>
的存储速度比List<T>
的速度要快
性能测试准备
既然要做性能测试,那我们肯定需要一个工具来统计代码块运行时间。C#里有现成的时间函数供我们使用,一般都是这么写:
1 2 3 4 5
| var time1 = System.DateTime.Now;
var time2 = System.DateTime.Now; var diff = time2.Subtract(time1); Console.WriteLine("Time Cost: {0}ms", diff.TotalMilliseconds);
|
有点儿麻烦,那我封装一下好了,成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class TimeCount { private DateTime m_startTime; private bool m_started;
public TimeCount() { m_started = false; m_startTime = System.DateTime.Now; }
public void start() { m_started = true; m_startTime = System.DateTime.Now; }
public double stop() { if (!m_started) { return 0; } TimeSpan diff = System.DateTime.Now.Subtract(m_startTime); double ret = diff.TotalMilliseconds; return ret; } };
|
直接调用Start和End就行
ArrayList
和List<object>
现在网上所有的说法都不赞同使用ArrayList
这个非泛型容器。理由是:
- 效率低下,涉及基本类型的保存时会产生装箱拆箱的操作,损失很大一部分性能
- 就算有时候我们有需要
List<object>
这样的容器,但是还是尽量写成List<object>
,写成ArrayList
会和其他List
显得不整齐
是这样没错,尤其是第二条,所以我们直接抛弃掉ArrayList
的写法实际上,ArrayList
和List<object>
的效率是一样的,用如下代码测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| System.Collections.ArrayList l0 = new System.Collections.ArrayList(); tc.start(); for (var i = 0; i < 1000000; ++i) { ClassData item = new ClassData(); item.intValue = i; l0.Add(item); } Console.WriteLine("Time Cost: {0}ms -- create l0", tc.stop());
List<object> l1 = new List<object>(); tc.start(); for (var i = 0; i < 1000000; ++i) { ClassData item = new ClassData(); item.intValue = i; l1.Add(item); } Console.WriteLine("Time Cost: {0}ms -- create l1", tc.stop());
|
输出结果如下:
于是我们就不再考虑ArrayList了
List<object>
和List<T>
这里的T是我们自定义的一个结构体或类
至于这里到底是结构体还是类,测试结果相差很远。
结构体
先看结构体的情况:
1 2 3 4
| struct StructData { public int intValue; };
|
接下来是测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| List<int> l3 = new List<int>(); tc.start(); for (var i = 0; i < 1000000; ++i) { l3.Add(i); } Console.WriteLine("Time Cost: {0}ms -- create l3", tc.stop());
List<object> l4 = new List<object>(); tc.start(); for (var i = 0; i < 1000000; ++i) { l4.Add(i); } Console.WriteLine("Time Cost: {0}ms -- create l4", tc.stop());
|
多执行几次,运行结果如下:
可以看到,当我们打算用List<T>
存储结构体时,最好不要用List<object>
,因为在存储的时候必须进行装箱的操作,也就是把存储于栈的结构体保存到堆中,这会极大地损耗性能
类
如果T不是结构体而是一个我们手动new出来的对象,那么理论上就不存在装箱和拆箱的操作了,然后再试试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class ClassData { public int intValue; };
List<object> l1 = new List<object>(); tc.start(); for (var i = 0; i < 1000000; ++i) { ClassData item = new ClassData(); item.intValue = i; l1.Add(item); } Console.WriteLine("Time Cost: {0}ms -- create l1", tc.stop());
List<ClassData> l2 = new List<ClassData>(); tc.start(); for (var i = 0; i < 1000000; ++i) { ClassData item = new ClassData(); item.intValue = i; l2.Add(item); } Console.WriteLine("Time Cost: {0}ms -- create l2", tc.stop());
|
同样多执行几次,运行结果如下:
咦,List<MyClass>
的速度居然还不如List<object>
我也不太清楚为啥会这样,估计是List<MyClass>
存的时候需要进行类型检查,这一步消耗了一定的时间吧
总结
其实这一篇也没啥实际意义,在实际的应用中还是尽量用List<MyClass>
吧,既不用考虑装箱导致的性能损耗,又能保证类型安全,损失的部分性能也是可以接受的,除非某些对应能特别敏感的需求。
PS. 新的发现,在List<object>
和List<T>
的对比中,使用macOS下的monoDevelop产生的结果会比较整齐,在Windows10下使用VS2015产生的结果会比较不整齐,统计出来的时间起伏较大
下一篇开始正儿八经做游戏啦