Welcome

首页 / 软件开发 / 汇编语言 / 从汇编入手,探究泛型的性能问题

从汇编入手,探究泛型的性能问题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获得托管方法的汇编代码》。