之前发布的question让我思考。在空列表中使用时,Any()
和Count()
会执行类似的操作吗?
正如here所述,两者都应该执行GetEnumerator()/MoveNext()/Dispose()
的相同步骤。
我在LINQPad上使用快速程序测试了这个:
static void Main()
{
var list = new List<int>();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 10000; i++)
list.Any();
stopwatch.Stop();
Console.WriteLine("Time elapsed for Any() : {0}", stopwatch.Elapsed);
stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 10000; i++)
list.Count();
stopwatch.Stop();
Console.WriteLine("Time elapsed for Count(): {0}", stopwatch.Elapsed);
}
一般结果似乎表明Count()
在这种情况下更快。那是为什么?
我不确定我是否获得了正确的基准,如果没有,我将不胜感激。
编辑:我知道它在语义上更有意义。我在问题中发布的第一个链接显示了直接使用Count()
是有意义的情况,因为会使用该值,因此问题。
答案 0 :(得分:22)
Count()
方法针对ICollection<T>
类型进行了优化,因此未使用模式GetEnumerator()/MoveNext()/Dispose()
。
list.Count();
翻译为
((ICollection)list).Count;
Any()
必须构建一个枚举器。
所以Count()
方法更快。
这里有4个不同IEnumerable
个实例的基准测试。 MyEmpty
看起来像IEnumerable<T> MyEmpty<T>() { yield break; }
iterations : 100000000
Function Any() Count()
new List<int>() 4.310 2.252
Enumerable.Empty<int>() 3.623 6.975
new int[0] 3.960 7.036
MyEmpty<int>() 5.631 7.194
正如casperOne在评论Enumerable.Empty<int>() is ICollection<int>
中所说的那样,因为它是一个数组,并且数组不适合Count()
扩展名,因为the cast to ICollection<int>
is not trivial.
无论如何,对于自制的空IEnumerable
,我们可以看到我们的预期,Count()
比Any()
慢,因为IEnumerable
测试的开销很大是ICollection
。
完成基准:
class Program
{
public const long Iterations = (long)1e8;
static void Main()
{
var results = new Dictionary<string, Tuple<TimeSpan, TimeSpan>>();
results.Add("new List<int>()", Benchmark(new List<int>(), Iterations));
results.Add("Enumerable.Empty<int>()", Benchmark(Enumerable.Empty<int>(), Iterations));
results.Add("new int[0]", Benchmark(new int[0], Iterations));
results.Add("MyEmpty<int>()", Benchmark(MyEmpty<int>(), Iterations));
Console.WriteLine("Function".PadRight(30) + "Any()".PadRight(10) + "Count()");
foreach (var result in results)
{
Console.WriteLine("{0}{1}{2}", result.Key.PadRight(30), Math.Round(result.Value.Item1.TotalSeconds, 3).ToString().PadRight(10), Math.Round(result.Value.Item2.TotalSeconds, 3));
}
Console.ReadLine();
}
public static Tuple<TimeSpan, TimeSpan> Benchmark(IEnumerable<int> source, long iterations)
{
var anyWatch = new Stopwatch();
anyWatch.Start();
for (long i = 0; i < iterations; i++) source.Any();
anyWatch.Stop();
var countWatch = new Stopwatch();
countWatch.Start();
for (long i = 0; i < iterations; i++) source.Count();
countWatch.Stop();
return new Tuple<TimeSpan, TimeSpan>(anyWatch.Elapsed, countWatch.Elapsed);
}
public static IEnumerable<T> MyEmpty<T>() { yield break; }
}