Linq to Objects: фильтрация вопроса о производительности - PullRequest
4 голосов
/ 13 октября 2010

Я думал о том, как linq вычисляет, и это заставило меня задуматься:

Если я напишу

var count = collection.Count(o => o.Category == 3);

Будет ли это работать иначе, чем:

var count = collection.Where(o => o.Category == 3).Count();

В конце концов, IEnumerable<T>.Where() вернет IEnumerable<T>, которое не реализует свойство Count, поэтому последующему Count() фактически придется пройтись по элементам, чтобы определить количество, которое должно вызвать дополнительное время, затрачиваемое на это.

Я написал небольшой тестовый код, чтобы получить некоторые метрики, но они, кажется, побеждают друг друга наугад.Сначала я не буду вставлять здесь тестовый код, но если кто-нибудь попросит, я его получу.

Итак, я что-то упустил?

Ответы [ 4 ]

3 голосов
/ 13 октября 2010

На самом деле в нем не будет много - обе формы будут перебирать коллекцию, проверять предикат для каждого элемента и подсчитывать совпадения. Оба подхода выполняют потоковую передачу данных - это не значит, что Where фактически создает список всех совпадений в памяти, например.

Первая форма имеет на один меньший (тонкий) слой косвенности, вот и все. Основной причиной его использования (IMO) является удобочитаемость / простота, а не производительность.

2 голосов
/ 13 октября 2010

Как говорит Джон Скит, оба метода должны будут по сути делать одно и то же - перечислять последовательность при условном увеличении счетчика при совпадении предиката.Любые различия в производительности между ними должны быть незначительными: незначительными практически для всех вариантов использования.Однако, если есть токен-победитель, я бы подумал , что он должен быть первым, поскольку из рефлектора кажется, что перегрузка Count, которая принимает предикат, использует свой собственный foreach для перечисления, а не дляболее очевидный способ перевести работу на потоковую передачу Where в без параметров Count, как во втором примере.Это означает, что метод № 1, вероятно, имеет две незначительные преимущества производительности:

  1. Меньше проверок аргументов (нулевые тесты и т. Д.) Проверок.Техника # 2 Count также проверит, является ли ее (переданный по каналу) вход ICollection или ICollection<T>, что не может быть.
  2. Один построенный перечислитель против двух перечислителей, соединенных вместе (у дополнительного конечного автомата есть затраты).

Есть один минор в пользу техники # 2, хотя: Where немного сложнее при создании перечислителя для исходной последовательности;он использует другой для списков и массивов.Это может сделать его более производительным в определенных сценариях.

Конечно, я должен повторить, что я могу быть просто неправильным относительно моего анализа - рассуждения о производительности через статический коданализ, особенно когда различия могут быть незначительными, не очень хорошая идея.Есть только один способ выяснить это - измерить время выполнения для вашей конкретной установки.

К вашему сведению, источник, который я отражал, был из .NET 3.5 SP1.

0 голосов
/ 13 октября 2010

Приведенные выше ответы дают хорошие аргументы, но учтите также, что если вы нарушите какие-либо реализации Linq-To-X, которые откладывают выполнение (основным является Linq-Sql), параметры Expression, используемые в этих методах, могут привести к разным результатам.

0 голосов
/ 13 октября 2010

Я знаю, что вы думаете здесь.По крайней мере, я так думаю;Count() проверит, доступен ли Count как свойство, и просто вернет его, если так.В противном случае он должен перечислить элементы, чтобы получить возвращаемое значение.

Версия Count(), которая принимает предикат, тем не менее, всегда будет вызывать повторение коллекции, поскольку она должна делать это, чтобы увидетькакие из них совпадают.

...