今天很热闹啊。老赵的数组排序方法的性能比较(中):Array.Sort<T>实现分析,Ivony的 数组排序LINQ的性能优势初步分析 —— 不起眼的属性等有关linq性能文章让我忍不住写下此文章。
Ivony的提供的代码中,对数组的排序时采用自定义的PersonComparer,而linq排序使用的确是Comparer<int>.Default,代码分析如下:
数组排序
{
Array.Sort(array, new PersonComparer());
}
LINQ排序
{
var sorted =
( from person in array
orderby person.ID
select person ).ToList();
}
上述lambda表达式对应为varsorted = Enumerable.OrderBy<Person,int>(array,person =>person.ID).ToList()。通过reflector反汇编可以得到以下代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public static IOrderedEnumerable < TSource > OrderBy < TSource, TKey > ( this IEnumerable < TSource > source, Func < TSource, TKey > keySelector)
{
return new OrderedEnumerable < TSource, TKey > (source, keySelector, null , false );
}
// 内部类OrderedEnumerable的代码
internal class OrderedEnumerable < TElement, TKey > : OrderedEnumerable < TElement >
{
// Fields
internal IComparer < TKey > comparer;
internal bool descending;
internal Func < TElement, TKey > keySelector;
internal OrderedEnumerable < TElement > parent;
// Methods
internal OrderedEnumerable(IEnumerable < TElement > source, Func < TElement, TKey > keySelector, IComparer < TKey > comparer, bool descending)
{
if (source == null )
{
throw Error.ArgumentNull( " source " );
}
if (keySelector == null )
{
throw Error.ArgumentNull( " keySelector " );
}
base .source = source;
this .parent = null ;
this .keySelector = keySelector;
this .comparer = (comparer != null ) ? comparer : ((IComparer < TKey > ) Comparer < TKey > .Default);
this .descending = descending;
}
internal override EnumerableSorter < TElement > GetEnumerableSorter(EnumerableSorter < TElement > next)
{
EnumerableSorter < TElement > enumerableSorter = new EnumerableSorter < TElement, TKey > ( this .keySelector, this .comparer, this .descending, next);
if ( this .parent != null )
{
enumerableSorter = this .parent.GetEnumerableSorter(enumerableSorter);
}
return enumerableSorter;
}
}
在文章数组排序方法的性能比较(上):注意事项及试验,老赵测试得出的结果可以得知PersonComparer比Comparer<int>.Default排序慢。
在增加一个新的SortWithLinq2方法之后,release运行测试,我得到新的结果。
新代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
namespace Exam11
{
public static class CodeTimer
{
public static void Initialize()
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Time( "" , 1 , () => { });
}
public static void Time( string name, int iteration, Action action)
{
if (String.IsNullOrEmpty(name)) return ;
// warm up
action();
// 1.
ConsoleColor currentForeColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(name);
// 2.
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
int [] gcCounts = new int [GC.MaxGeneration + 1 ];
for ( int i = 0 ; i <= GC.MaxGeneration; i ++ )
{
gcCounts[i] = GC.CollectionCount(i);
}
// 3.
Stopwatch watch = new Stopwatch();
watch.Start();
ulong cycleCount = GetCycleCount();
for ( int i = 0 ; i < iteration; i ++ ) action();
ulong cpuCycles = GetCycleCount() - cycleCount;
watch.Stop();
// 4.
Console.ForegroundColor = currentForeColor;
Console.WriteLine( " \tTime Elapsed:\t " + watch.ElapsedMilliseconds.ToString( " N0 " ) + " ms " );
Console.WriteLine( " \tCPU Cycles:\t " + cpuCycles.ToString( " N0 " ));
// 5.
for ( int i = 0 ; i <= GC.MaxGeneration; i ++ )
{
int count = GC.CollectionCount(i) - gcCounts[i];
Console.WriteLine( " \tGen " + i + " : \t\t " + count);
}
Console.WriteLine();
}
private static ulong GetCycleCount()
{
ulong cycleCount = 0 ;
QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
return cycleCount;
}
[DllImport( " kernel32.dll " )]
[ return : MarshalAs(UnmanagedType.Bool)]
static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);
[DllImport( " kernel32.dll " )]
static extern IntPtr GetCurrentThread();
}
class Program
{
static void Main( string [] args)
{
var random = new Random(DateTime.Now.Millisecond);
var array = Enumerable.Repeat( 0 , 50000 ).Select(_ => new Person { ID = random.Next() }).ToArray();
JetBrains.dotTrace.Api.CPUProfiler.Start();
// 老赵程序
CodeTimer.Initialize();
CodeTimer.Time( " SortWithCustomComparer " , 500 , () => SortWithCustomComparer(CloneArray(array)));
CodeTimer.Time( " SortWithLinq2 " , 500 , () => SortWithLinq(CloneArray(array)));
CodeTimer.Time( " SortWithLinq " , 500 , () => SortWithLinq2(CloneArray(array)));
JetBrains.dotTrace.Api.CPUProfiler.StopAndSaveSnapShot();
}
public static void Time( string name, int iteration, Action action)
{
action();
Console.WriteLine(name);
for ( int i = 0 ; i < iteration; i ++ ) action();
}
private static readonly PersonComparer comparer = new PersonComparer();
private static void SortWithCustomComparer(Person[] array)
{
Array.Sort(array, comparer);
}
private static void SortWithLinq(Person[] array)
{
// array = (from person in array
// orderby person.ID
// select person).ToList();
Enumerable.OrderBy < Person, int > (array,person => person.ID).ToList();
}
private static void SortWithLinq2(Person[] array)
{
// array = (from person in array
// orderby person.ID
// select person).ToList();
Enumerable.OrderBy < Person, Person > (array, person => person, comparer).ToList();
}
private static T[] CloneArray < T > (T[] source)
{
var dest = new T[source.Length];
Array.Copy(source, dest, source.Length);
return dest;
}
}
public class Person
{
public string FirstName
{
get ;
set ;
}
public string LastName
{
get ;
set ;
}
public int ID
{
get ;
set ;
}
public override int GetHashCode()
{
return this .ID.GetHashCode();
}
}
public class PersonComparer : IComparer < Person >
{
public int Compare(Person x, Person y)
{
return x.ID - y.ID;
}
}
}
不使用dottrace探测得到测试结果(使用dottrace之后运行态慢了,而且不好得到控制台的显示结果,除非使用辅助软件):
SortWithCustomComparer
Time Elapsed: 11,446ms
CPU Cycles: 26,201,674,700
Gen 0: 31
Gen 1: 31
Gen 2: 31
SortWithLinq
Time Elapsed: 14,970ms
CPU Cycles: 34,259,890,929
Gen 0: 311
Gen 1: 310
Gen 2: 187
SortWithLinq2
Time Elapsed: 18,573ms
CPU Cycles: 42,491,287,773
Gen 0: 311
Gen 1: 311
Gen 2: 187
使用dottrace的分析图如如下(启动dottrace后程序运行的时间比之前的慢多了):
上述结果测试表明,在使用PersonComparer之后SortWithLinq2比SortWithLinq慢了差不多一倍。在此也印证老赵的相关分析。