分享
 
 
 

实现分析

王朝学院·作者佚名  2010-01-28
窄屏简体版  字體: |||超大  

昨天我们比较了Array.Sort<T>方法与LINQ排序的性能,知道了LINQ排序的性能以较大幅度落后于Array.Sort<T>方法。而对于Array.Sort<T>来说,性能最高的是其中使用Comparer<int>.Default作为比较器的重载方法。在前文的末尾我们做出了推测:由于排序算法已经近乎一个标准了(快速排序),因此从算法角度来说,Array.Sort<T>方法和LINQ排序上不应该有那么大的差距,因此造成两者性能差异的原因,应该是具体实现方式上的问题。

下载.NET框架的代码

既然是比较实现的区别,那么阅读代码是很直接的选择。谈到阅读.NET代码,我们往往会使用.NET Reflector将框架的程序集反编译为C#代码——不排除有朋友喜欢观察IL,并认为它们更直接,更底层。不过我倒觉得在绝大部分情况下,IL能看出的东西从C#也能看出,而C#无法了解的IL也帮不上忙,因此许多“由IL发现问题”的文章其实只是自己和自己在爽。

不过,虽然.NET Reflector将程序集反编译成非常优秀的C#代码,但是还是无法恢复之前的不少信息。例如,局部变量名无法得知,这就给理解代码意图造成了困难。再例如,为了可读性我们可能会将一些条件语句分开写,而C#编译器则可能把它们连做一块儿。至于注释等明显会被去除的东西就更不用说了。因此,在能直接读到代码的情况下,我并不倾向于使用.NET Reflector。

您可能会说:这不是废话么,不过有些类库——如.NET框架并没有提供源代码,这又有什么办法呢?其实微软也已经公布了.NET框架相当部分程序集的源代码(几乎所有v2.0的程序集,如mscrolib,System,System.Web等等),而且它们其实都可以使用NetMassDownloader直接下载到本地。随员代码发布的还有方便调试的pdb文件,不过这是另一个话题了,我们现在只关心源代码。

因此,我建议您将所有微软提供的源代码都下载到本地。在看不懂.NET Reflector的反编译结果时,不妨参考一下源代码——还是包含注释的源代码。

Array.Sort<T>方法实现

各Array.Sort<T>方法的重载最终都会委托给下面这个重载来执行:

public static void Sort<T>(T[] array, int index, int length, IComparer<T> comparer)

{

...

if (length > 1)

{

// <STRIP>

// TrySZSort is still faster than the generic implementation.

// The reason is Int32.CompareTo is still expensive than just using "<" or ">".

// </STRIP>

if (comparer == null || comparer == Comparer<T>.Default)

{

if (TrySZSort(array, null, index, index + length - 1))

{

return;

}

}

ArraySortHelper<T>.Default.Sort(array, index, length, comparer);

}

}我们知道,从逻辑上说,Array.Sort<T>方法会使用IComparer<T>类型的比较器来判断两个元素的大小。不过在这里,.NET框架作了一个“特例”,它在用户没有提供比较器,或是直接使用默认比较器的时候利用TrySZSort方法进行排序。如果TrySZSort方法如果返回true,则表示排序完成,直接返回。如果它返回false,则继续使用内置的排序方法进行处理。那么TrySZSort又是如何实现的呢?

private static extern bool TrySZSort(Array keys, Array items, int left, int right);这是一个extern方法,说明它是由CLR直接实现的,我们无法得知它的具体算法或是意图。不够从注释中可以得知,这个做法的目的是提高性能(明白注释的优势了吧)。每次使用IComparer<T>的Compare方法进行比较的时候相当于是一次虚方法的调用,CLR需要计算它的偏移量,也无法将其内联。这个细节相对于直接进行int的大小比较来说,也是有较大开销的。使用TrySZSort这种外部方法进行排序,有助于提高在特定情况下的执行效率。

因此,我们应该可以有足够信心来推断出TrySZSort的作用。TrySZSort方法的作用是对一些可以直接进行比较的原生类型(如int等)进行排序,如果它发现自己无法支持数组中元素的类型,那么就返回false,否则便排序后并返回true。如果TrySZSort返回false,便会使用ArraySortHelper进行排序。而其中的实现便是标准(真的很标准)的快速排序,如果您感兴趣的话可以自行阅读其中的代码。

值得注意的是,Array是定义在System命名空间下的类型,而ArraySortHelper则定义在System.Collections.Generic命名空间下。在阅读微软提供的源代码时有个麻烦之处便是不容易导航,例如ArraySortHelper类便让我一顿好找。不过,其实我们也可以配合.NET Reflector中强大的导航功能来快速定位到某个类的位置,然后直接去查找它对应的文件。当然,如果您索引了文件内容,也可以查找类似“class ArraySortHelper”这样的关键字,也可以很快找到特定文件。

Array.Sort<T>其他重载的性能

以上,我们已经知道为什么使用Comparer<int>.Default作为比较器时性能最高了,因为对于int类型来说,Array.Sort<T>方法会使用最快的TrySZSort方法进行排序。而如果我们使用自定义的Int32Comparer进行比较的话,Compare方法调用的开销是无可避免的,根据实验结果,使用Int32Comparer的执行时间比前者有80%的增加也可以接受。

那么使用委托进行排序的时候为什么比Int32Comparer更慢一些呢?且看其代码:

public static void Sort<T>(T[] array, Comparison<T> comparison)

{

...

IComparer<T> comparer = new FunctorComparer<T>(comparison);

Sort<T>(array, comparer);

}其实原因很简单,因为.NET框架使用了FunctorComparer封装了comparison委托。这样在每次比较时,它相对于Int32Comparer来说还增加了委托执行的开销——这又相当于一次虚方法的调用:需要寻址,无法内联。

至此,我们已经分析了Array.Sort<T>各重载的实现方式,也了解了三种Array.Sort<T>重载性能有别的原因。刚好,不久前姜敏兄又回应了我的文章,认为使用Person类,而不是int类型进行比较的时候,仍旧是LINQ排序比较快——由此他认为两种做法的性能和类型也有关系。您认为这个说法正确吗?其实从实现上看,Array.Sort<T>几乎已经是最优的做法了。相反,LINQ排序由于会生成额外的序列,性能想要超过Array.Sort<T>几乎是件不可能的事情。但事实真是如此吗?

那这测试结果……我也写了一个Person类的测试(http://gist.github.com/282796),还是Array.Sort<T>比较快。那么为什么两个结果会有所不同呢?这是一个值得探讨的问题。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有