WEB开发网      濠电姷鏁告慨鐑藉极閸涘﹦绠鹃柍褜鍓氱换娑欐媴閸愬弶鎼愮痪鍓ф嚀閳规垿鎮╃€圭姴顥濋梺姹囧€楅崑鎾诲Φ閸曨垰绠涢柛顐f礃椤庡秹姊虹粙娆惧剳闁哥姵鍔欐俊鐢稿礋椤栨艾鍞ㄩ梺闈浤涙担鎻掍壕闁圭儤顨嗛埛鎺楁煕閺囥劌浜滄い蹇e弮閺屸€崇暆鐎n剛鏆犻柧浼欑到閵嗘帒顫濋悡搴d画缂佹鍨垮缁樻媴缁涘娈┑顔斤公缁犳捇銆佸鎰佹▌濠电姭鍋撳ù锝囩《閺€浠嬫煟濡鍤嬬€规悶鍎辫灃闁绘ê寮堕崯鐐电磼閸屾氨效鐎规洘绮忛ˇ瀵哥棯閹佸仮鐎殿喖鐖煎畷鐓庘槈濡警鐎崇紓鍌欑劍椤ㄥ棗鐣濋幖浣歌摕闁绘棃顥撻弳瀣煟濡も偓閻楀棗鈻撳Δ鍛拺閻犲洠鈧櫕鐏€闂佸搫鎳愭慨鎾偩閻ゎ垬浜归柟鐑樼箖閺呮繈姊洪棃娑氬婵☆偅鐟╅、娆掔疀閺冨倻鐦堥梺姹囧灲濞佳勭閿曞倹鐓曢柕濞垮劤閸╋絾顨ラ悙鏉戝妤犵偞锕㈤、娆撴嚃閳哄骞㈤梻鍌欐祰椤鐣峰Ο鑲╃煋妞ゆ棁锟ユ禍褰掓煙閻戞ɑ灏ù婊冪秺濮婅櫣绱掑Ο铏逛桓闂佹寧娲嶉弲娑滅亱闂佸憡娲﹂崹閬嶅煕閹达附鐓欓柤娴嬫櫅娴犳粌鈹戦垾鐐藉仮闁诡喗顨呴埥澶愬箳閹惧褰囩紓鍌欑贰閸犳牠顢栭崨鎼晣闁稿繒鍘х欢鐐翠繆椤栨粎甯涙繛鍛喘濮婄粯鎷呴悷閭﹀殝缂備浇顕ч崐鍨嚕缂佹ḿ绡€闁搞儯鍔嶅▍鍥⒑缁嬫寧婀扮紒瀣崌瀹曘垽鎮介崨濠勫幗闁瑰吋鐣崹濠氬煀閺囥垺鐓ユ慨妯垮煐閻撶喖鐓崶銉ュ姢缂佸宕电槐鎺旂磼濡偐鐣虹紓浣虹帛缁诲牆鐣峰鈧俊姝岊槺缂佽鲸绻堝缁樻媴缁涘娈愰梺鎼炲妺閸楀啿鐣烽鐐茬骇闁瑰濮靛▓楣冩⒑缂佹ɑ鈷掗柍宄扮墦瀵偊宕掗悙瀵稿幈闂佹娊鏁崑鎾绘煛閸涱喚鎳呮俊鍙夊姇铻i悶娑掑墲閺傗偓闂備胶绮崝鏇炍熸繝鍥у惞闁绘柨鐨濋弨鑺ャ亜閺冨洦顥夐柛鏂诲€濋幗鍫曟倷閻戞ḿ鍘遍梺鍝勬储閸斿本鏅堕鐐寸厱婵炲棗绻掔粻濠氭煛鐏炵晫效鐎规洦鍋婂畷鐔碱敆閳ь剙鈻嶉敐鍥╃=濞达絾褰冩禍鐐節閵忥絾纭炬い鎴濇川缁粯銈i崘鈺冨幍闁诲孩绋掑玻璺ㄧ不濮椻偓閺屻劌鈽夊Ο澶癸絾銇勯妸锝呭姦闁诡喗鐟╅、鏃堝礋椤撴繄绀勯梻鍌欐祰椤曟牠宕伴弽顐ょ濠电姴鍊婚弳锕傛煙椤栫偛浜版俊鑼额嚙閳规垿鍩勯崘銊хシ濡炪値鍘鹃崗妯侯嚕鐠囨祴妲堥柕蹇曞閳哄懏鐓忓璺虹墕閸旀挳鏌涢弬娆炬Ш缂佽鲸鎸婚幏鍛矙鎼存挸浜鹃柛婵勫劤閻挾鎲搁悧鍫濈瑨闁哄绶氶弻鐔煎礈瑜忕敮娑㈡煛閸涱喗鍊愰柡灞诲姂閹倝宕掑☉姗嗕紦 ---闂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏃堟暜閸嬫挾绮☉妯哄箻婵炲樊浜滈悡娑㈡煕濞戝崬骞樻い鏂挎濮婅櫣鎹勯妸銉︾彚闂佺懓鍤栭幏锟�
开发学院WEB开发ASP 数组排序方法的性能比较(1):注意事项及试验 阅读

数组排序方法的性能比较(1):注意事项及试验

 2010-01-27 10:46:56 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹妞嬪孩顐芥慨姗嗗厳缂傛氨鎲稿鍫罕闂備礁婀遍搹搴ㄥ窗閺嶎偆涓嶆い鏍仦閻撱儵鏌i弴鐐测偓鍦偓姘炬嫹闂傚倸鍊搁崐鎼佸磹妞嬪海鐭嗗〒姘e亾妤犵偛顦甸弫鎾绘偐閹绘帞鈧參姊哄Ч鍥х仼闁诲繑鑹鹃悾鐑藉蓟閵夛妇鍘甸梺瑙勵問閸犳牠銆傛總鍛婄厱閹艰揪绱曟牎闂侀潧娲ょ€氫即鐛幒妤€绠f繝闈涘暙娴滈箖鏌i姀鈶跺湱澹曟繝姘厵闁绘劦鍓氶悘杈ㄤ繆閹绘帞澧涚紒缁樼洴瀹曞崬螖閸愬啠鍓濈换娑樼暆婵犱胶鏁栫紓浣介哺閹瑰洤鐣烽幒鎴僵闁瑰吀鐒﹂悗鎼佹⒒娴g儤鍤€闁搞倖鐗犻獮蹇涙晸閿燂拷濠电姷鏁告慨鐑藉极閸涘﹥鍙忔い鎾卞灩缁狀垶鏌涢幇闈涙灈鐎瑰憡绻冮妵鍕箻鐎靛摜鐣奸梺纭咁潐濞茬喎顫忕紒妯肩懝闁逞屽墮宀h儻顦查悡銈夋煏閸繃鍋繛宸簻鎯熼梺瀹犳〃閼冲爼宕濋敃鈧—鍐Χ閸℃鐟愰梺鐓庡暱閻栧ジ宕烘繝鍥у嵆闁靛骏绱曢崢顏堟⒑閹肩偛鍔楅柡鍛⊕缁傛帟顦寸紒杈ㄥ笚濞煎繘鍩℃担閿嬵潟闂備浇妗ㄩ悞锕傚箲閸ヮ剙鏋侀柟鍓х帛閺呮悂鏌ㄩ悤鍌涘闂傚倸鍊搁崐鎼佸磹妞嬪孩顐芥慨姗嗗厳缂傛氨鎲稿鍫罕闂備礁婀遍搹搴ㄥ窗閺嶎偆涓嶆い鏍仦閻撱儵鏌i弴鐐测偓鍦偓姘炬嫹  闂傚倸鍊搁崐鎼佸磹閻戣姤鍤勯柤鍝ユ暩娴犳氨绱撻崒娆掑厡缂侇噮鍨堕妴鍐川閺夋垹鍘洪悗骞垮劚椤︻垶宕¢幎鑺ョ厪闊洦娲栨牎闂佽瀵掗崜鐔奉潖閾忓湱纾兼俊顖氭惈椤矂姊洪崷顓涙嫛闁稿妫濋幆鈧い蹇撴祩濡嫰姊洪崫鍕拱婵炲弶岣块幑銏犫攽婵犲嫮鏉搁梺鍝勬川婵兘鎮伴妷鈺傗拻濞达絽鎼敮璺侯熆閻熷府鏀荤紒鍌氱Т楗即宕煎锝呬壕闁哄啫鐗嗙粈鍐┿亜韫囧海顦﹀ù婊堢畺閺屻劌鈹戦崱娑扁偓妤€顭胯閸犳牠婀侀梺缁樕戦悷銉р偓姘煎枤缁粯銈i崘鈺冨幈濡炪倖鍔戦崐鏇㈠几鎼淬劍鐓熼煫鍥ь儏閸旀粓鏌曢崶褍顏€殿喗娼欒灒闁告繂瀚濠碉紕鍋戦崐鎴﹀垂濞差亝鍋¢柍鍝勬噹缁犳牠鏌嶉埡浣告殲闁稿海鍠栭弻鏇㈠炊瑜嶇花濠氭煙閸戙倖瀚�
核心提示:昨天有朋友写了一篇文章,其中比较了List<T>的Sort方法与LINQ中排序方法的性能,数组排序方法的性能比较(1):注意事项及试验,而最终得到的结果是“LINQ排序方法性能高于List<T>.Sort方法”,这个结果不禁让我很疑惑,我们下次再来进行分析——这些方法的实现,尤其是LINQ排序的
昨天有朋友写了一篇文章,其中比较了List<T>的Sort方法与LINQ中排序方法的性能,而最终得到的结果是“LINQ排序方法性能高于List<T>.Sort方法”。这个结果不禁让我很疑惑。因为List<T>.Sort方法是改变容器内部元素的顺序,而LINQ排序后得到的是一个新的序列。假如两个排序方法的算法完全一致,LINQ排序也比对方多出元素复制的开销,为什么性能反而会高?如果LINQ排序的算法/实现更为优秀,那为什么.NET Fx不将List<T>.Sort也一并优化一下呢?于是今天我也对这个问题进行了简单的试验。

注意事项
在后面的评论中有人说,List<T>.Sort是“内部排序”,而LINQ排序是“外部排序”。但是根据外部排序的定义,这个说法是不正确的。“外部排序”是指在排序目标规模太大,导致主存相对太小(如内存)而不够放,不得不利用外部存储(如硬盘)做一些“过渡”的排序方式。因此,LINQ排序虽然生成了新的序列,但还是内部排序。事实上,从定义中我们也可以很容易推断出,如果数据规模相同,外部排序的性能一般总是比内部排序要差——不过事实上我们不太好做这样的比较,因为如果是能够进行内部排序的情况下,谁会利用麻烦的外部排序呢?

那篇文章中得到的结果是不对的,那么问题究竟出在什么地方呢?在我看来,问题主要出在以下两点。

首先,原文作者使用了asp.net作为测试环境。值得注意的是,ASP.NET执行.NET代码的时候,使用的是IIS进程中托管的CLR,它的配置和直接运行.NET应用程序时不同(不同的CLR托管方式配置很可能不一样——例如SQL Server里托管的CLR)。例如,ASP.NET中每个线程的调用栈为250K,而直接运行.NET应用程序时这个大小为1M。根据我的经验(也就是说我无法确切地“证明”这个说法),在ASP.NET中执行此类性能测试得到的结果可能很不稳定。因此,一般我建议使用一个最普通的Console应用程序来进行性能测试。

其次,也是更重要的原因,便是原作者只测试了一次排序的性能。这是性能测试中的大忌讳,因为这么做的话误差实在太大。例如,会不会在进行某一个方法的测试时,忽然系统起了一个后台进程进行维护,动用了一部分CPU和内存资源,从而导致测试消耗的时间很冤枉地增加。而且,.NET程序是有一个“预热”过程的,这导致代码在第一次执行时需要有一个生成本机代码的过程(俗称“预热”)。这个过程和代码的执行效率是无关的,因为它无论如何只会产生一次消耗,而代码是会被执行无数次的。因此,在进行测试的时候,一定要将测试目标反复执行N次,至少要让执行耗时到达“秒”级别,这样的结果才有一定参考价值。如果执行时间太少的话测试也可能不准确——要知道“计时器”也是有开销,也是有误差的,我们要得到尽量准确的结果。

最后,我强烈建议使用CodeTimer进行计时,因为在它的Initialize方法中会将当前进程及线程的优先级设置到最高,以此减少其他无关因素所造成的干扰。如果可以的话,其实也应该尽量关闭系统中其他应用程序或服务,并且最好可以断开网络(也是一个道理)。当然Release编译也是一定需要的。而且,如果您一定需要使用ASP.NET进行性能测试的话,也千万记得要在web.config中将<compilation />节点的debug属性设为false——考虑到原作者忽略了之前犯了很明显的忌讳,我强烈怀疑这点也没有满足。:)

因此,我认为那篇文章中的测试结果是不准确的,参考价值很低。

重新测试
既然如此,我们来重新进行一次试验吧。不过我现在来比较的是Array.Sort<T>方法与LINQ排序的性能。这么做的主要原因是,由于我们要执行多次排序操作,因此每次都应该使用乱序的序列。但是,每次生成新的序列也会带来开销,如果使用List<T>的话,填充元素的开销会很大,但如果是普通数组的话,就可以用性能很高的Array.Copy方法了:

static T[] CloneArray<T>(T[] source)
{
  var dest = new T[source.Length];
  Array.Copy(source, dest, source.Length);
  return dest;
}从试验结果上看,数组的复制操作应该并没有造成显著影响。还有便是,经过阅读List<T>.Sort方法的代码,我们可以发现它只是将实现简单地委托给Array.Sort<T>方法,因此我们可以认为它们的性能完全一致。

Array.Sort<T>方法其实有两“类”重载,一类是使用IComparer<T>对象进行比较,而另一类则使用了Comparison<T>这个委托对象进行比较。为此,我们构造一个Int32Comparer类来实现IComparer<int>接口:

PRivate class Int32Comparer : IComparer<int>
{
  #region IComparer<int> Members

  public int Compare(int x, int y)
  {
    return x - y;
  }

  #endregion
}于是,两“类”重载的测试方法分别为:

static void SortWithCustomComparer(int[] array)
{
  Array.Sort(array, new Int32Comparer());
}

static void SortWithDelegate(int[] array)
{
  Array.Sort(array, (x, y) => x - y);
}但是,我在这里还是再补充一个排序“方法”(原因以后再谈)。因为int类型是框架知道该如何比较的类型,因此我们其实也可以这么写:

static void SortWithDefaultComparer(int[] array)
{
  Array.Sort(array, Comparer<int>.Default);
}最后,参加活动的自然还有LINQ排序:

static void SortWithLinq(int[] array)
{
  var sorted =
    (from i in array
     orderby i
     select i).ToList();
}这里我使用的是ToList方法而不是ToArray方法来生成新序列,这是因为ToList的方法性能更高一些,我还是想尽可能将关注点放在“排序”而不是其他方面。

比较代码如下:

static void Main(string[] args)
{
  var random = new Random(DateTime.Now.Millisecond);
  var array = Enumerable.Repeat(0, 1000 * 500).Select(_ => random.Next()).ToArray();

  CodeTimer.Initialize();

  CodeTimer.Time("SortWithDefaultComparer", 100,
    () => SortWithDefaultComparer(CloneArray(array)));

  CodeTimer.Time("SortWithCustomComparer", 100,
    () => SortWithCustomComparer(CloneArray(array)));

  CodeTimer.Time("SortWithDelegate", 100,
    () => SortWithDelegate(CloneArray(array)));

  CodeTimer.Time("SortWithLinq", 100,
    () => SortWithLinq(CloneArray(array)));

  Console.ReadLine();
}首先,我们生成一个拥有50万个随机元素的数组,以此进行排序方法的性能比较。每次比较的时候我们都使用CloneArray方法来生成一个新的数组。

试验结果
试验结果如下:

SortWithDefaultComparer
    Time Elapsed:  7,662ms
    Gen 0:     49
    Gen 1:     49
    Gen 2:     49

SortWithCustomComparer
    Time Elapsed:  13,847ms
    Gen 0:     49
    Gen 1:     49
    Gen 2:     49

SortWithDelegate
    Time Elapsed:  19,625ms
    Gen 0:     49
    Gen 1:     49
    Gen 2:     49

SortWithLinq
    Time Elapsed:  31,785ms
    Gen 0:     99
    Gen 1:     99
    Gen 2:     99从结果上来,四种做法的性能区别还是非常明显的:使用Comparer<int>.Default进行排序的耗时只有LINQ排序的1/4。有趣的是,从GC次数上来看,LINQ排序也刚好是其他三种的一倍,和“理论值”几乎完全吻合。

经过测试后发现,其实LINQ排序的性能并不会比Array.Sort<T>要高,甚至还有明显的劣势。由于排序算法都已经被用到滥了,而且几乎已经成为了一个“标准”,因此从算法上来看它们不应该会有那么大的差距。所以,我们这里应该可以得出一个比较靠谱的推测:这几种排序方法的性能差距,完全是由于具体实现上的细节引起的。

至于其中的原因,我们下次再来进行分析——这些方法的实现,尤其是LINQ排序的实现,似乎还是挺有趣的。

Tags:数组 排序 方法

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接