今天很热闹啊。老赵的数组排序方法的性能比较(中):Array.Sort<T>实现分析,Ivony的 数组排序LINQ的性能优势初步分析 —— 不起眼的属性等有关linq性能文章让我忍不住写下此文章。
Ivony的提供的代码中,对数组的排序时采用自定义的PersonComparer,而linq排序使用的确是Comparer<int>.Default,代码分析如下:
数组排序
private
static
void
SortWithCustomComparer(Person[] array)
{
Array.Sort(array,
new
PersonComparer());
}
LINQ排序
private
static
void
SortWithLinq( Person[] array )
{
var sorted
=
( from person
in
array
orderby person.ID
select person ).ToList();
}
上述lambda表达式对应为varsorted = Enumerable.OrderBy<Person,int>(array,person =>person.ID).ToList()。通过reflector反汇编可以得到以下代码
代码
//
Enumerable类中OrderBy的代码
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运行测试,我得到新的结果。
新代码:
代码
using
System;
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慢了差不多一倍。在此也印证老赵的相关分析。