今天很热闹啊。老赵的数组排序方法的性能比较(中):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)
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)
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慢了差不多一倍。在此也印证老赵的相关分析。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
· 用 C# 插值字符串处理器写一个 sscanf