• List<T>在排序上的性能测试


          看了老赵写的性能测试文章(数组排序方法的性能比较(上):注意事项及试验),的确长了不少见识,不过我对他的结论也不完全同意,为此针对我的测试环境进行一翻改造。 

          第一条:ASP.NET执行.NET代码的时候,使用的是IIS进程中托管的CLR,它的配置和直接运行.NET应用程序时不同(不同的CLR托管方式配置很可能不一样——例如SQL Server里托管的CLR)。

                   所以我放弃asp.net,选用Console应用程序来进行性能测试。

          第二条:更改数据结果,生成一个序号号分散的List<T>。

               1:生成一个大的随机数组:
               

    var random = new Random(DateTime.Now.Millisecond);
    var array 
    = Enumerable.Repeat(0500001).Select(_ => random.Next()).ToArray();

           

              2:利用随机数组的元素做为我们测试用的List<T>子元素Person类的ID

               

    代码
                List<Person> list = new List<Person>();
                
    for (int i = 0; i < 500001; i++)
                {
                    Person p 
    = new Person();
                    p.firstName 
    = i.ToString() + "firstName";
                    p.lastName 
    = i.ToString() + "lastName";
                    p.ID 
    = array[i];
                    list.Add(p);

                }
       

          第三条:增加测试次数。之前我只排序一次,这里我来排序5次。

               

    代码
                int iCount=5;
                Stopwatch sw 
    = new Stopwatch();
                sw 
    = new Stopwatch();
                sw.Start();
                
    for (int i = 0; i < iCount; i++)
                {
                    list.Sort(
    new PersonComparer());
                }
                sw.Stop();
                Console.WriteLine(
    "Sort排序用时" + sw.ElapsedMilliseconds.ToString());

                sw 
    = new Stopwatch();
                sw.Start();
                
    for (int i = 0; i < iCount; i++)
                {
                    var pn 
    = (from m in list
                              orderby m.ID descending
                              select m).ToList
    <Person>();
                }
                sw.Stop();
                Console.WriteLine(
    "linq排序用时" + sw.ElapsedMilliseconds.ToString());

        

          第四条:增加release版本的测试。

       

          说明:代码中相关类如下:

        

    代码
        public class Person
        {
            
    public string firstName
            { 
    getset; }
            
    public string lastName
            { 
    getset; }
            
    public int ID
            { 
    getset; }
        }
        
    public class PersonComparer : IComparer<Person>
        {
            
    public int Compare(Person x, Person y)
            {
                
    return x.ID.CompareTo(y.ID);
            }

        }

          测试结果:还是和之前的结果一样,LINQ在排序上明显有优势。


           我们来换一种测试,我是对List<T>来进行测试,其中的T是一个对象,并不是数值性的,老赵文中排序的是array[int],下面代码中的array就是之前生成的无序数组。测试代码如下:

               

    代码
                sw = new Stopwatch();
                sw.Start();
                
    for (int i = 0; i < iCount; i++)
                {
                    SortWithDefaultComparer(array);
                }
                sw.Stop();
                Console.WriteLine(
    "Sort 数组排序用时" + sw.ElapsedMilliseconds.ToString());

                sw 
    = new Stopwatch();
                sw.Start();
                
    for (int i = 0; i < iCount; i++)
                {
                    SortWithLinq(array);
                }
                sw.Stop();
                Console.WriteLine(
    "linq 数组排序用时" + sw.ElapsedMilliseconds.ToString());
        

      

          测试结果:和老赵文中一样,LINQ会差很多。
       
          比较:老赵和我的测试环境还是一个特别大的差别,他在文中有这段话:每次生成新的序列也会带来开销,如果使用List<T>的话,填充元素的开销会很大,但如果是普通数组的话,就可以用性能很高的Array.Copy方法了。
                 问题就在这,我的方法中是对Person类排序,排序字段为ID,而老赵排序的是int[],这里我再写一个测试,测试Array<T>的性能,当然这个T并非int,而是Person。
       
          1:我们先创建一个Person[],这个Person[]的ID和上面一样,是无序的。
               

    代码
                Person[] alist = new Person[500001];
                
    for (int i = 0; i < 500001; i++)
                {
                    Person p 
    = new Person();
                    p.firstName 
    = i.ToString() + "firstName";
                    p.lastName 
    = i.ToString() + "lastName";
                    p.ID 
    = array[i];
                    alist[i] 
    = p;

                }
         

            2:调用Array.Sort方法。
             

    代码
            static void SortWithDefaultComparerList(Person []  array)
            {           

                Array.Sort(array, 
    new PersonComparer());
            }
            
    static T[] CloneArray<T>(T[] source)
            {
                var dest 
    = new T[source.Length];
                Array.Copy(source, dest, source.Length);
                
    return dest;
            }
            
                sw 
    = new Stopwatch();
                sw.Start();
                
    for (int i = 0; i < iCount; i++)
                {
                    SortWithDefaultComparerList(CloneArray(alist ));
                }
                sw.Stop();
                Console.WriteLine(
    "Arr数组排序用时" + sw.ElapsedMilliseconds.ToString());

           

           测试结果:同样是LINQ方法更优。

           总结:对于List<T>的排序,从性能角度来看:

                  1:T的类型会对结果产生重要影响,并不能片面的说LINQ方式排序更强于或者差于List.Sort或者是Array.Sort。

                  2:当T的属性越来越多时, LINQ方式的优势会更加明显。          

           本文可执行代码见文末:

                 老赵的codertimer执行情况和我的测试结果一样。

                 下图是debug下的结果,linq 效果明显,release版本下,linq稍微强一点点,相差无几。

                    release版本

    代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Collections;
    using System.Diagnostics;
    using System.Threading;
    using System.Runtime.InteropServices;
    namespace ConsoleApplication1
    {
        
    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(0500001).Select(_ => random.Next()).ToArray();
                List
    <Person> list = new List<Person>();
                
    for (int i = 0; i < 500001; i++)
                {
                    Person p 
    = new Person();
                    p.firstName 
    = i.ToString() + "firstName";
                    p.lastName 
    = i.ToString() + "lastName";
                    p.ID 
    = array[i];
                    list.Add(p);

                }
                Person[] alist 
    = new Person[500001];
                
    for (int i = 0; i < 500001; i++)
                {
                    Person p 
    = new Person();
                    p.firstName 
    = i.ToString() + "firstName";
                    p.lastName 
    = i.ToString() + "lastName";
                    p.ID 
    = array[i];
                    alist[i] 
    = p;

                }
                
    int iCount = 5;
                Stopwatch sw 
    = new Stopwatch();
                

                sw 
    = new Stopwatch();
                sw.Start();
                
    for (int i = 0; i < iCount; i++)
                {
                    List
    <Person> newlist = list ;
                    SortWithLinqList(newlist );
                }
                sw.Stop();
                Console.WriteLine(
    "linq 数组排序用时" + sw.ElapsedMilliseconds.ToString());

                sw 
    = new Stopwatch();
                sw.Start();
                
    for (int i = 0; i < iCount; i++)
                {
                    SortWithDefaultComparerList(CloneArray(alist));
                }
                sw.Stop();
                Console.WriteLine(
    "Arr数组排序用时" + sw.ElapsedMilliseconds.ToString());

                
    //老赵程序
                var randoms = new Random(DateTime.Now.Millisecond);
                var arrays 
    = Enumerable.Repeat(01000 * 500).Select(_ => new Person { ID = random.Next(),firstName="aaa",lastName ="bbb" }).ToArray();
                CodeTimer.Initialize();
                CodeTimer.Time(
    "SortWithCustomComparer"5, () => SortWithCustomComparer(CloneArray(arrays)));
                CodeTimer.Time(
    "SortWithLinq"5, () => SortWithLinq(CloneArray(arrays)));
                Console.ReadLine();
                Console.ReadKey();
            }
            
    static void SortWithCustomComparer(Person[] array)
            {
                Array.Sort(array, 
    new PersonComparer());
            }
            
    static void SortWithDefaultComparer(int[] array)
            {
                Array.Sort(array, Comparer
    <int>.Default);
            }

            
    static void SortWithLinq(Person[] array)
            {
                var sorted 
    =
                    (from i 
    in array
                     orderby i.ID
                     select i).ToList();
            }

            
    static void SortWithDefaultComparerList(Person[] array)
            {
                Array.Sort(array, 
    new PersonComparer());
            }
            
    static T[] CloneArray<T>(T[] source)
            {
                var dest 
    = new T[source.Length];
                Array.Copy(source, dest, source.Length);
                
    return dest;
            }
            
    static void SortWithLinqList(List<Person> list)
            {
                var sorted 
    =
                    (from i 
    in list
                     orderby i.ID
                     select i).ToList();
            }

           
        }
        
    public class Person
        {
            
    public string firstName
            { 
    getset; }
            
    public string lastName
            { 
    getset; }
            
    public int ID
            { 
    getset; }
            
    public string lastName2
            { 
    getset; }
            
    public string lastName3
            { 
    getset; }
            
    public string lastName4
            { 
    getset; }
            
    public string lastName5
            { 
    getset; }
            
    public string lastName6
            { 
    getset; }
            
    public string lastName7
            { 
    getset; }
            
    public string lastName8
            { 
    getset; }
            
    public string lastName9
            { 
    getset; }
            
    public string lastName10
            { 
    getset; }
        }
        
    public class PersonComparer : IComparer<Person>
        {
            
    public int Compare(Person x, Person y)
            {
                
    return x.ID - y.ID;
            }

        }
    }

           List<T>在搜索和排序时采用不同方法的性能比较

     

  • 相关阅读:
    常见的单链表题目
    SpringBoot Hello
    IDEA 重置
    lombok的用法
    软件测试系列白盒测试覆盖率的问题
    软件测试系列软件测试基础
    Linux常用命令1对文件进行查看、复制、移动和分割
    软件测试系列通用测试用例写作
    Java继承特性
    Linux常用命令3如何设置IP地址?如何更改系统时间?
  • 原文地址:https://www.cnblogs.com/ASPNET2008/p/1653636.html
Copyright © 2020-2023  润新知