using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; public class Test { Main() { IEnumerable<int> tenMillionToZero = Enumerable.Range(0, 10_000_000).Reverse(); while (true) { var sw = Stopwatch.StartNew(); int fifth = tenMillionToZero.OrderBy(i => i).Skip(4).First(); Console.WriteLine(sw.Elapsed); } } }
在这里,我们创建一个可以从10,000,000下降到0的数字,然后再等待一会来排序它们上升,跳过排序结果中的前4个元素,并抓住第五个。在个人计算机上的NET 4.7中得到如下输出:
::00:01.3548279
而使用.NET Core 2.0,会得到如下输出:
::00:00.1735489
这是一个巨大的改进(8x),避免了大部分的开销。
类似地,来自justinvp的 PR dotnet / corefx#3429对常用的ToList方法添加了优化,为已知长度的源,提供了优化的路径,并且通过像Select这样的操作器来管理。在以下简单测试中,这种影响是显而易见的:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; public class Test { Main() { IEnumerable<int> tenMillionToZero = Enumerable.Range(0, 10_000_000).Reverse(); while (true) { var sw = Stopwatch.StartNew(); int fifth = tenMillionToZero.OrderBy(i => i).Skip(4).First(); Console.WriteLine(sw.Elapsed); } } }
在.NET 4.7中,会得到如下结果:
::00:00.1503511
而在.NET Core 2.0中,得到如下结果:
::00:00.0355355
显示吞吐量增加约4倍。
在其他情况下,性能优势来自于简化实施,以避免开销,例如减少分配,避免委托分配,避免接口调用,最小化字段读取和写入,避免拷贝等。例如,jamesqo为PR dotnet / corefx#11208做出的贡献,大大地减少了Enumerable.ToArray涉及的开销。请看下面的例子:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; public class Test { Main() { IEnumerable<int> zeroToTenMillion = Enumerable.Range(0, 10_000_000).ToArray(); while (true) { var sw = Stopwatch.StartNew(); zeroToTenMillion.Select(i => i).ToList(); Console.WriteLine(sw.Elapsed); } } }
在.NET 4.7中,会得到如下的结果:
Elapsed=00:00:01.0548794 Gen0=2 Gen1=2 Gen2=2 Elapsed=00:00:01.1147146 Gen0=2 Gen1=2 Gen2=2 Elapsed=00:00:01.0709146 Gen0=2 Gen1=2 Gen2=2 Elapsed=00:00:01.0706030 Gen0=2 Gen1=2 Gen2=2 Elapsed=00:00:01.0620943 Gen0=2 Gen1=2 Gen2=2
而.NET Core 2.0的结果如下:
Elapsed=00:00:00.1716550 Gen0=1 Gen1=1 Gen2=1 Elapsed=00:00:00.1720829 Gen0=1 Gen1=1 Gen2=1 Elapsed=00:00:00.1717145 Gen0=1 Gen1=1 Gen2=1 Elapsed=00:00:00.1713335 Gen0=1 Gen1=1 Gen2=1 Elapsed=00:00:00.1705285 Gen0=1 Gen1=1 Gen2=1
这个例子中提高了6倍,但是垃圾收集却只有一半。
LINQ有一百多个运算器,本文只提到了几个,其它的很多也都有所改进。
压缩
前面所展示的集合和LINQ的例子都是处理内存中的数据,当然还有许多其他形式的数据处理,包括大量CPU计算和逻辑判断,这些运算也在得到提升。