不同IComparer对数组排序Array.Sort,Linq orderby的性能的影响

      今天很热闹啊。老赵的数组排序方法的性能比较(中):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慢了差不多一倍。在此也印证老赵的相关分析。

 

 

你可能感兴趣的:(compare)