找回密码
 立即注册
首页 业界区 业界 范型真的会降低性能吗?

范型真的会降低性能吗?

赙浦 2025-5-29 20:29:16
在《.NET,你忘记了么?(八)—— 从dynamic到特性误用》一文中,飞林沙同学提到,使用泛型会略微降低程序性能,因此在程序中使用List是不合理的行为,应该使用ArrayList。这一点和老赵平时的观点相悖,老赵一直提倡,在.NET 2.0之后,要尽可能使用List,情愿是List也不要使用ArrayList。不过个中原因与性能无关,我们稍候再叙述。飞同学的文章让我有了将泛型与非泛型进行性能比较的想法。这个比较非常容易,不过也得出了一些非常有意思的结论。
泛型容器与非泛型容器的性能比较

首先,我们来比较一种最“纯粹”的泛型容器,它的目的是避免程序的其他方面对性能的影响。因此,这里我们构造两个最简单的容器,就是简单的模仿ArrayList和List:
  1. public class MyArrayList
  2. {
  3.     public MyArrayList(int length)
  4.     {
  5.         this.m_items = new object[length];
  6.     }
  7.     public object[] m_items;
  8.     public object this[int index]
  9.     {
  10.         get
  11.         {
  12.             return this.m_items[index];
  13.         }
  14.         set
  15.         {
  16.             this.m_items[index] = value;
  17.         }
  18.     }
  19.     public IEnumerable InnerContainer
  20.     {
  21.         get
  22.         {
  23.             return this.m_items;
  24.         }
  25.     }
  26. }
  27. public class MyList<T>
  28. {
  29.     public MyList(int length)
  30.     {
  31.         this.m_items = new T[length];
  32.     }
  33.     public T[] m_items;
  34.     public T this[int index]
  35.     {
  36.         get
  37.         {
  38.             return this.m_items[index];
  39.         }
  40.         set
  41.         {
  42.             this.m_items[index] = value;
  43.         }
  44.     }
  45.     public IEnumerable<T> InnerContainer
  46.     {
  47.         get
  48.         {
  49.             return this.m_items;
  50.         }
  51.     }
  52. }
复制代码
MyArrayList为直接使用Object类型的容器,而MyList则是泛型容器。老赵为他们实现了两种操作,一是下标访问,二是直接把内部的数组容器作为可遍历的对象释放出来。这两种都是平时编程中最经常使用的操作。
于是我们就可以编写测试代码了。首先,我们初始化两种容器,并进行“预热”:
  1. int length = 1000;
  2. object value = new object();
  3. MyArrayList myArrayList = new MyArrayList(length);
  4. for (int i = 0; i < length; i++)
  5. {
  6.     myArrayList[i] = myArrayList[i] ?? value;
  7. }
  8. MyList<object> myList = new MyList<object>(length);
  9. for (int i = 0; i < length; i++)
  10. {
  11.     myList[i] = myList[i] ?? value;
  12. }
复制代码
然后我们使用CodeTimer来进行统计:
  1. CodeTimer.Initialize();
  2. int iteration = 300 * 1000;
  3. CodeTimer.Time("MyArrayList下标访问", iteration, () =>
  4. {
  5.     for (int i = 0; i < length; i++)
  6.     {
  7.         var o = myArrayList[i];
  8.     }
  9. });
  10. CodeTimer.Time("MyList下标访问", iteration, () =>
  11. {
  12.     for (int i = 0; i < length; i++)
  13.     {
  14.         var o = myList[i];
  15.     }
  16. });
复制代码
Release Build并运行。猜猜看,结果是什么?
  1. <strong>MyArrayList下标访问</strong>
  2.         Time Elapsed:   2,398ms
  3.         CPU Cycles:     5,042,561,997
  4.         Gen 0:          0
  5.         Gen 1:          0
  6.         Gen 2:          0
  7. <strong>MyList下标访问</strong>
  8.         Time Elapsed:   2,285ms
  9.         CPU Cycles:     4,935,066,741
  10.         Gen 0:          0
  11.         Gen 1:          0
  12.         Gen 2:          0
复制代码
以上是在老赵的机器上得到的结果,从结果上看,泛型的MyList性能甚至略比MyArrayList有所提高。当然测试的结果其实是互有胜负,但是事实上,MyList的获胜的次数甚至还略有领先。
那么我们再来看看“遍历”的性能如何:
  1. CodeTimer.Time("MyArrayList遍历", iteration, () =>
  2. {
  3.     foreach (object o in myArrayList.InnerContainer)
  4.     {
  5.         var o1 = o;
  6.     }
  7. });
  8. CodeTimer.Time("MyList遍历", iteration, () =>
  9. {
  10.     foreach (object o in myList.InnerContainer)
  11.     {
  12.         var o1 = o;
  13.     }
  14. });
复制代码
运行的结果颇有意思:
  1. <strong>MyArrayList遍历</strong>
  2.         Time Elapsed:   21,367ms
  3.         CPU Cycles:     46,023,627,496
  4.         Gen 0:          2
  5.         Gen 1:          1
  6.         Gen 2:          0
  7. <strong>MyList遍历</strong>
  8.         Time Elapsed:   3,463ms
  9.         CPU Cycles:     7,448,928,223
  10.         Gen 0:          2
  11.         Gen 1:          0
  12.         Gen 2:          0
复制代码
直接使用Object的MyArrayList性能居然差了这么多!个中原因老赵有所猜测,在得到明确答案之后会接着与大家分享。
ArrayList和List的性能

刚才比较了最“纯粹”的性能,那么我们再来比较ArrayList和List。因为我们其实不知道它俩具体在实现上的细节是如何的,还是比较一下这两个容器具体的性能比较好。比较的内容还是两项:下标访问及遍历。代码如下:
  1. int length = 1000;
  2. object value = new object();
  3. ArrayList arrayList = new ArrayList(length);
  4. for (int i = 0; i < length; i++)
  5. {
  6.     arrayList.Add(value);
  7.     arrayList[i] = arrayList[i] ?? value;
  8. }
  9. List<object> list = new List<object>(length);
  10. for (int i = 0; i < length; i++)
  11. {
  12.     list.Add(value);
  13.     list[i] = list[i] ?? value;
  14. }
  15. CodeTimer.Initialize();
  16. int iteration = 300 * 1000;
  17. CodeTimer.Time("ArrayList下标访问", iteration, () =>
  18. {
  19.     for (int i = 0; i < length; i++)
  20.     {
  21.         var o = arrayList[i];
  22.     }
  23. });
  24. CodeTimer.Time("List下标访问", iteration, () =>
  25. {
  26.     for (int i = 0; i < length; i++)
  27.     {
  28.         var o = list[i];
  29.     }
  30. });
  31. CodeTimer.Time("ArrayList遍历", iteration, () =>
  32. {
  33.     foreach (object o in arrayList)
  34.     {
  35.         var o1 = o;
  36.     }
  37. });
  38. CodeTimer.Time("List遍历", iteration, () =>
  39. {
  40.     foreach (object o in list)
  41.     {
  42.         var o1 = o;
  43.     }
  44. });
复制代码
结果如下:
  1. <strong>ArrayList下标访问</strong>
  2.         Time Elapsed:   2,282ms
  3.         CPU Cycles:     4,838,476,797
  4.         Gen 0:          0
  5.         Gen 1:          0
  6.         Gen 2:          0
  7. <strong>List下标访问</strong>
  8.         Time Elapsed:   2,302ms
  9.         CPU Cycles:     4,979,267,920
  10.         Gen 0:          0
  11.         Gen 1:          0
  12.         Gen 2:          0
  13. <strong>ArrayList遍历</strong>
  14.         Time Elapsed:   5,187ms
  15.         CPU Cycles:     11,145,830,014
  16.         Gen 0:          4
  17.         Gen 1:          0
  18.         Gen 2:          0
  19. <strong>List遍历</strong>
  20.         Time Elapsed:   2,989ms
  21.         CPU Cycles:     6,459,825,955
  22.         Gen 0:          0
  23.         Gen 1:          0
  24.         Gen 2:          0
复制代码
现在您还觉得泛型会降低性能,List比ArrayList的性能差吗?
总结

从结果上已经可以看出,泛型并不会影响性能,而List的性能也不比ArrayList要差。因此老赵继续坚持:在有泛型支持的情况下,尽量使用泛型容器。例如使用List而不是ArrayList。除了“性能”之外,老赵的还有其他一些理由。例如使用List的话就可以使用框架内部所定义的各种有用的辅助方法(要知道在.NET框架中,现在几乎都是在针对IEnumerable进行开发);而我们平时写程序时,也可以统一的针对泛型编程,如IList,IEnumerable,不必考虑List或Enumerable等非泛型元素。
放心使用List吧。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册