.net泛型,数组,lambda调用性能 定量比较

本文探讨了.NET框架中泛型、数组和Lambda表达式在排序性能上的差异。作者通过编写不同版本的快速排序算法进行测试,发现.NET LINQ的Sort方法性能最优。实验结果显示,使用Lambda表达式会降低性能,而从List转换为数组可以显著提高排序速度。此外,调整函数调用结构也会对性能产生影响。

这几天准备.net的面试 周末手写快速排序练练手,一时兴起想测测自己写的快速排序性能如何,尤其是对比以前写过的快速排序,发现了.net性能上的一点儿有趣的事情

首先是一段高扩展性的快速排序,支持泛型,同时还使用lambda表达式使得该快速排序可以支持任何类型的排序,只要提供了排序方法即可,代码如下

public static void Sort<T>(List<T> arrayForSort, int startIndex, int endIndex, Func<T, T, bool> CompareMethod)
        {
            if (startIndex >= endIndex)
            {
                return;
            }
            int pointer = startIndex;
            int end = endIndex;
            for (int i = pointer + 1; i <= end; i++)
            {
                if (CompareMethod(arrayForSort[pointer], arrayForSort[i]))//把比当前锚点数小的数全部移到锚点数左边
                {
                    var temp = arrayForSort[pointer];
                    arrayForSort[pointer] = arrayForSort[i];
                    arrayForSort[i] = temp;
                    pointer = i;
                }
                else//若当前比较的数比锚点数大 则将其与最后面的数对调
                {
                    var temp = arrayForSort[end];
                    arrayForSort[end] = arrayForSort[i];
                    arrayForSort[i] = temp;
                    --i;
                    --end;
                }
            }
            Sort<T>(arrayForSort, startIndex, pointer, CompareMethod);
            Sort<T>(arrayForSort, pointer+1, endIndex, CompareMethod);
        }

然后同样的排序方法,写法也一样,但是去掉了方便扩展的泛型支持以及lambda,这样代码就只能对list<int>排序了代码如下(其实就是把参数改了下,删除了最后的CompareMethod参数)

public static void Sort(List<int> arrayForSort, int startIndex, int endIndex)
        {
            if (startIndex >= endIndex)
            {
                return;
            }
            int pointer = startIndex;
            int end = endIndex;
            for (int i = pointer + 1; i <= end; i++)
            {
                if (arrayForSort[pointer] > arrayForSort[i])//把比当前锚点数小的数全部移到锚点数左边
                {
                    var temp = arrayForSort[pointer];
                    arrayForSort[pointer] = arrayForSort[i];
                    arrayForSort[i] = temp;
                    pointer = i;
                }
                else//若当前比较的数比锚点数大 则将其与后面的数对调
                {
                    var temp = arrayForSort[end];
                    arrayForSort[end] = arrayForSort[i];
                    arrayForSort[i] = temp;
                    --i;
                    --end;
                }
            }
            Sort(arrayForSort, startIndex, pointer);
            Sort(arrayForSort, pointer + 1, endIndex);
        }
然后进一步简化 讲list<int>改为int[]数组,代码不用动,只需要改参数就可以了

public static void Sort(int[] arrayForSort, int startIndex, int endIndex)
        {
            if (startIndex >= endIndex)
            {
                return;
            }
            int pointer = startIndex;
            int end = endIndex;
            for (int i = pointer + 1; i <= end; i++)
            {
                if (arrayForSort[pointer] > arrayForSort[i])//把比当前锚点数小的数全部移到锚点数左边
                {
                    var temp = arrayForSort[pointer];
                    arrayForSort[pointer] = arrayForSort[i];
                    arrayForSort[i] = temp;
                    pointer = i;
                }
                else//若当前比较的数比锚点数大 则将其与后面的数对调
                {
                    var temp = arrayForSort[end];
                    arrayForSort[end] = arrayForSort[i];
                    arrayForSort[i] = temp;
                    --i;
                    --end;
                }
            }
            Sort(arrayForSort, startIndex, pointer);
            Sort(arrayForSort, pointer + 1, endIndex);
        }

最后是快排的另一种写法,同样是用int[]做容器(其实我已经不怎么看得懂当时是如何写的了。。。。。)

 public static void SortAnotherMethod(int[] source, int startPoint, int endPoint)
        {
            if (startPoint < endPoint)
            {
                int i = partrn(source, startPoint, endPoint);
                SortAnotherMethod(source, startPoint, i - 1);
                SortAnotherMethod(source, i + 1, endPoint);
            }
        }
        private static void swap(int[] source, int left, int right)
        {
            source[left] = source[right] + source[left];
            source[right] = source[left] - source[right];
            source[left] = source[left] - source[right];
        }
        private static int partrn(int[] source, int left, int right)
        {
            int pivot = source[left];
            while (left < right)
            {
                while (left < right && source[right] >= pivot)
                    right--;
                if (left < right)
                {
                    swap(source, left, right);
                }
                while (left < right && source[left] <= pivot)
                    left++;
                if (left < right)
                {
                    swap(source, left, right);
                }
            }
            source[left] = pivot;
            return left;
        }  

最后是相对简单的调用代码。。利用stopwatch计时,这里利用快速排序对一千万个数进行排序 数字范围在一亿里面 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace QuickSort
{
    class Program
    {
        static void Main(string[] args)
        {
            Random rdm = new Random();
            List<int> testList = new List<int>();
            List<int> testList2 = new List<int>();
            int[] testArray4 = new int[10000000];
            List<int> testList3 = new List<int>();
            int[] testArray3 = new int[10000000];
            for (int i = 0; i < 10000000; i++)
            {
                var number = rdm.Next(100000000);
                testList.Insert(i, number);
                testList2.Insert(i, number);
                testList3.Insert(i, number);
                testArray3[i] = number;
                testArray4[i] = number;
            }
            var watch1 = new Stopwatch();
            watch1.Start();
            QuickSortMain.Sort<int>(testList, 0, testList.Count-1, (x, y) => { return x > y; });
            watch1.Stop();
            var customSort = watch1.ElapsedMilliseconds;

            watch1.Reset();
            watch1.Start();
            QuickSortMain.Sort(testList2, 0, testList2.Count - 1);
            watch1.Stop();
            var customSort2 = watch1.ElapsedMilliseconds;

            watch1.Reset();
            watch1.Start();
            QuickSortMain.Sort(testArray3, 0, testArray3.Length - 1);
            watch1.Stop();
            var customSort3 = watch1.ElapsedMilliseconds;

            watch1.Reset();
            watch1.Start();
            QuickSortMain.SortAnotherMethod(testArray4, 0, testArray4.Length - 1);
            watch1.Stop();
            var customSort4 = watch1.ElapsedMilliseconds;

            watch1.Reset();
            watch1.Start();
            testList3.Sort();
            watch1.Stop();
            var systemSort = watch1.ElapsedMilliseconds;
            Console.WriteLine("泛型方法,涉及lambda调用       Custom1 Sort:" + customSort);//快排,比较方法用了lambda,容器为list<int>
            Console.WriteLine("直接list<int>做容器                  Custom2 Sort:" + customSort2);//直接用list<int>做容器
            Console.WriteLine("直接int[]做容器                         Custom3 Sort:" + customSort3);//容器为Int[],写法上和1,2是一样的
            Console.WriteLine("直接int[]做容器的另一种写法     Custom4 Sort:" + customSort4);//容器同样为int[], 算法同样是快速排序但是写法略有不同
            Console.WriteLine("C#自带排序方法                       System Sort:" + systemSort);//系统自带排序
            Console.ReadLine();
        }
    }
}

激动人心的时刻到了 让我们来看看各种调用方法在时间上面的差异


很不幸自己写的方法全部败给了.net linq自带的sort(改天可以研究研究)

这里看看自己的排序方法之间的差异

第一种 扩展性最强 泛型+lambda可以应付基本上所有排序(当然.net自带的也可以这么做 但是是使用的ICompare接口,我个人认为是直接用lambda来比较方便),时间上惨不忍睹。。。和.net linq差了一个数量级还多

第二种 去掉了lambda调用,速度立马飞升三分之一,可见lambda对性能的影响是很明显的(其实任何函数调用都影响性能,这就是为啥c++里面简短的函数编译时候可以内联)

第三种 将list<int>改为了int[],速度又提升一半,很明显数组的直接内存地址读取要比list通过地址来读取快一倍(改天试试用hashtable做容器跟int[]做对比)

第四种 还是int[]做容器,只是排序算法写法稍微做了改动,本质上还是快速排序,可以看出,和最开始的唯一不同多了几步函数调用,可见函数调用对性能的影响

第五章 .net linq自带的sort......不说啥了。。。有谁知道内部是如何实现的么?



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值