Почему LINQ .Where (предикат) .First () быстрее, чем .First (предикат)? - PullRequest
68 голосов
/ 29 декабря 2011

Я провожу некоторые тесты производительности и заметил, что выражение LINQ вроде

result = list.First(f => f.Id == i).Property

медленнее, чем

result = list.Where(f => f.Id == i).First().Property

Это кажется нелогичным. Я бы подумал, что первое выражение будет быстрее, потому что оно может прекратить итерации по списку, как только предикат будет удовлетворен, тогда как я бы подумал, что выражение .Where() может перебирать весь список перед вызовом .First() на результирующее подмножество. Даже если последний делает короткое замыкание, оно не должно быть быстрее, чем использование First напрямую, но это так.

Ниже приведены два действительно простых модульных теста, которые иллюстрируют это. При компиляции с оптимизацией в TestWhereAndFirst примерно на 30% быстрее, чем TestFirstOnly в .Net и Silverlight 4. Я пытался заставить предикат возвращать больше результатов, но разница в производительности такая же.

Может ли кто-нибудь объяснить, почему .First(fn) медленнее, чем .Where(fn).First()? Я вижу аналогичный интуитивно понятный результат с .Count(fn) по сравнению с .Where(fn).Count().

private const int Range = 50000;

private class Simple
{
   public int Id { get; set; }
   public int Value { get; set; }
}

[TestMethod()]
public void TestFirstOnly()
{
   List<Simple> list = new List<Simple>(Range);
   for (int i = Range - 1; i >= 0; --i)
   {
      list.Add(new Simple { Id = i, Value = 10 });
   }

   int result = 0;
   for (int i = 0; i < Range; ++i)
   {
      result += list.First(f => f.Id == i).Value;
   }

   Assert.IsTrue(result > 0);
}

[TestMethod()]
public void TestWhereAndFirst()
{
   List<Simple> list = new List<Simple>(Range);
   for (int i = Range - 1; i >= 0; --i)
   {
      list.Add(new Simple { Id = i, Value = 10 });
   }

   int result = 0;
   for (int i = 0; i < Range; ++i)
   {
      result += list.Where(f => f.Id == i).First().Value;
   }

   Assert.IsTrue(result > 0);
}

1 Ответ

49 голосов
/ 29 декабря 2011

Я получил те же результаты: где + первый был быстрее, чем первый.

Как отметил Джон, Linq использует ленивую оценку, поэтому производительность должна быть (и является) в целом одинаковой для обоих методов.

Глядя в Reflector, First использует простой цикл foreach для итерации по коллекции, но где имеется множество итераторов, предназначенных для различных типов коллекций (массивов, списков и т. Д.). Предположительно это то, что дает Где маленькое преимущество.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...