Cyr*_*don 24 .net c# linq performance ienumerable
使用扩展方法时IEnumerable<T> Count(),数组至少比列表慢两倍.
Function Count()
List<int> 2,299
int[] 6,903
Run Code Online (Sandbox Code Playgroud)
差异来自哪里?
据我所知,两者都调用Count的属性ICollection:
如果源的类型实现ICollection,则该实现用于获取元素的数量.否则,此方法确定计数.
对于它返回的列表List<T>.Count,对于数组,Array.Length.而且,Array.Length应该比...更快List<T>.Count.
基准测试:
class Program
{
public const long Iterations = (long)1e8;
static void Main()
{
var list = new List<int>(){1};
var array = new int[1];
array[0] = 1;
var results = new Dictionary<string, TimeSpan>();
results.Add("List<int>", Benchmark(list, Iterations));
results.Add("int[]", Benchmark(array, Iterations));
Console.WriteLine("Function".PadRight(30) + "Count()");
foreach (var result in results)
{
Console.WriteLine("{0}{1}", result.Key.PadRight(30), Math.Round(result.Value.TotalSeconds, 3));
}
Console.ReadLine();
}
public static TimeSpan Benchmark(IEnumerable<int> source, long iterations)
{
var countWatch = new Stopwatch();
countWatch.Start();
for (long i = 0; i < iterations; i++) source.Count();
countWatch.Stop();
return countWatch.Elapsed;
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:
leppie和Knaģis的答案非常惊人,但我想补充一句话.
正如Jon Skeet所说:
实际上有两个等效的块,只是测试不同的集合接口类型,并使用它首先找到的任何一个(如果有的话).我不知道.NET实现是否首先测试ICollection或ICollection <T> - 我可以通过实现两个接口来测试它,但当然可以从每个接口返回不同的计数,但这可能是过度的.除了轻微的性能差异之外,对于性能良好的集合并不重要 - 我们希望首先测试"最可能"的接口,我认为这是通用接口.
通用的可能是最有可能发生的,但是如果你反转这两个,即在通用的之前调用非泛型强制转换,Array.Count()变得比List.Count()快一点.另一方面,List的非通用版本较慢.
很Count()高兴知道是否有人想要在1e8迭代循环中调用!
Function ICollection<T> Cast ICollection Cast
List 1,268 1,738
Array 5,925 1,683
Run Code Online (Sandbox Code Playgroud)
原因是Enumerable.Count<T>()执行强制转换ICollection<T>以从列表和数组中检索计数.
使用此示例代码:
public static int Count<TSource>(IEnumerable<TSource> source)
{
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
{
return 1; // collection.Count;
}
}
Run Code Online (Sandbox Code Playgroud)
你可以确定演员阵容需要花费更长的时间,实际上大部分时间用于计算:
Function Count()
List<int> 1,575
int[] 5,069
Run Code Online (Sandbox Code Playgroud)
关键可能是文档中的这个陈述(强调我的):
在.NET Framework 2.0版中,Array类实现System.Collections.Generic.IList,System.Collections.Generic.ICollection和System.Collections.Generic.IEnumerable通用接口.这些实现在运行时提供给数组,因此文档构建工具不可见.因此,通用接口不会出现在Array类的声明语法中,并且没有可通过将数组转换为通用接口类型(显式接口实现)来访问的接口成员的参考主题.
32位分析分析(全部以ms为单位,仅有趣的位,JIT内联禁用):
Name Count 'Inc Time' 'Ex Time' 'Avg Inc Time' 'Avg Ex Time'
System.Linq.Enumerable::Count(<UNKNOWN>):int32 <System.Int32>
20000000 13338.38 7830.49 0.0007 0.0004
System.SZArrayHelper::get_Count():int32 <System.Int32>
10000000 4063.9 2651.44 0.0004 0.0003
System.Collections.Generic.List<System.Int32>::get_Count():int32
10000000 1443.99 1443.99 0.0001 0.0001
System.Runtime.CompilerServices.JitHelpers::UnsafeCast(Object):System.__Canon <System.__Canon>
10000004 1412.46 1412.46 0.0001 0.0001
Run Code Online (Sandbox Code Playgroud)
System.SZArrayHelper::get_Count()似乎要求System.Runtime.CompilerServices.JitHelpers::UnsafeCast数组的情况.
对于列表,List<int>.Count只需返回大小.
Inc time费用包括儿童电话费.
Ex time只是方法体的成本.
禁用内联时,Array.Count()速度是两倍.
这可能是因为提到现在已删除的答案.看起来应用的属性(ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)和 SecuritySafeCritical)会阻止运行时内联调用,因此差异很大(在32位模式下,我的情况要慢38倍).
要自己分析一下:
获取https://github.com/leppie/IronScheme/raw/master/IronScheme/tools/IronScheme.Profiler.x86.dll 运行应用程序(仅限x86 build):
regsvr32 IronScheme.Profiler.x86.dll
set COR_PROFILER={9E2B38F2-7355-4C61-A54F-434B7AC266C0}
set COR_ENABLE_PROFILING=1
ConsoleApp1.exe
Run Code Online (Sandbox Code Playgroud)
当应用程序退出时,report.tab会创建一个文件,然后可以在Excel中使用该文件.
| 归档时间: |
|
| 查看次数: |
1269 次 |
| 最近记录: |