Ответ 1
Метод 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()
, потому что приведение в ICollection<int>
не является тривиальным.
В любом случае, для самодельного пустого 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; }
}