从汇编入手,探究泛型的性能问题2012-09-29 博客园 老赵经过了《泛型真的会降低性能吗?》一文中的性能测试,已经从实际入手,从 测试数据上证明了泛型不会降低程序效率。只是还是有几位朋友谈到,“普遍认 为”泛型的代码性能会略差一些,也有朋友正在进一步寻找泛型性能略差的证据 。老赵认为这种探究问题的方式非常值得提倡。不过,老赵忽然想到,如果从能 从汇编入手,证明非泛型和泛型的代码之间没有性能差距——好吧,或者说,存 在性能差距,那么事情不就到此为止了吗?任何理论说明,都抵不过观察计算机 是如何处理这个问题来的“直接”。因此,老赵最终决定通过这种极端的方式来 一探究竟,把这个问题彻底解决。需要一提的是,老赵并不希望这篇文章会引起一些不必要的争论,因此一些话 就先说在前面。老赵并不喜欢用这种方式来解决问题。事实上,如果可以通过数 据比较,理论分析,或者高级代码来说明问题,我连IL都不愿意接触,更别说深 入汇编。如果是平时的工作,就算使用WinDbg也最多是查看查看内存中有哪些数 据,系统到底出了哪些问题。如果您要老赵表态的话,我会说:我强烈反对接触 汇编。我们有太多太多的东西需要学习,如果您并没有明确您的目标,老赵建议 您就放过IL和汇编这种东西吧。我们知道这些是什么就行了,不必对它们有什么 “深入”的了解。下面就要开始真正的探索之旅了。这不是一个顺利的旅程,其中有些步骤是连 蒙带猜,最后加以验证才得到的结果。原本老赵打算按照自己的思路一步一步进 行下去,但是发现这样太过冗余,反而会让大家的思路难以集中。因此老赵最后 决定重新设计一个流程,和大家一起步步为营,朝着目标前进。此外,为了方便 某些朋友按照这文章亲手进行操作,老赵也制作了一个dump文件,如果您是安装 了.NET 3.5 SP1的32位x86系统,可以直接下载进行试验。试验过程中出现的地址 也会和文章中完全一致。废话就说到这里,我们开始吧。测试代码测试代码便是我们的目标。和上一篇文章一样,我们准备了一份最简单的代码 进行测试,这样可以尽可能摆脱其他因素的影响,得到最正确的结果:
namespace TestConsole{ public class MyArrayList { public MyArrayList(int length) { this.m_items = new object[length]; } private object[] m_items; public object this[int index] { [MethodImpl(MethodImplOptions.NoInlining)] get { return this.m_items[index]; } [MethodImpl(MethodImplOptions.NoInlining)] set { this.m_items[index] = value; } } } public class MyList<T> { public MyList(int length) { this.m_items = new T[length]; } private T[] m_items; public T this[int index] { [MethodImpl(MethodImplOptions.NoInlining)] get { return this.m_items[index]; } [MethodImpl(MethodImplOptions.NoInlining)] set { this.m_items[index] = value; } } } class Program { static void Main(string[] args) { MyArrayList arrayList = new MyArrayList(1); arrayList[0] = arrayList[0] ?? new object(); MyList<object> list = new MyList<object>(1); list[0] = list[0] ?? new object(); Console.WriteLine("Here comes the testing code."); var a = arrayList[0]; var b = list[0]; Console.ReadLine(); } }}
我们在这里构建了两个“容器”,一个是MyArrayList,另一个是 MyList<T>,前者直接使用Object类型,而后者则是一个泛型类。我们对两 个类的索引属性的get和set方法都加上了NoInlining标记,这样便可以避免这种 简单的方法被JIT内联。而在Main方法中,前几行代码的作用都是构造两个类的对 象,并确保索引的get和set方法都已经得到JIT。在打印出“Here comes the testing code.”之后,我们便对两个类的实例进行“下标访问”,并使控制台暂 停。当Release编译并运行之后,控制台会打印出“Here comes the testing code.”字样并停止。这时候我们便可以使用WinDbg来Attach to Process进行调 试。老赵也是在这个时候制作了一个dump文件,您也可以Open Crash Dump命令打 开这个文件。更多操作您可以参考互联网上的各篇文章,亦或是老赵之前写过的 一篇《使用WinDbg获得托管方法的汇编代码》。