Означает ли порядок функций LINQ?

В принципе, поскольку в вопросе говорится, что порядок функций LINQ имеет значение performance? Очевидно, что результаты должны быть идентичны еще...

Пример:

myCollection.OrderBy(item => item.CreatedDate).Where(item => item.Code > 3);
myCollection.Where(item => item.Code > 3).OrderBy(item => item.CreatedDate);

Оба возвращают мне те же результаты, но находятся в другом порядке LINQ. Я понимаю, что переупорядочение некоторых предметов приведет к разным результатам, и меня это не касается. Основная моя забота заключается в том, что при получении одинаковых результатов заказ может повлиять на производительность. И не только на 2 вызовах LINQ, которые я сделал (OrderBy, Where), но и на любые вызовы LINQ.

Ответ 1

Это будет зависеть от используемого поставщика LINQ. Для LINQ to Objects это может иметь огромное значение. Предположим, что мы действительно получили:

var query = myCollection.OrderBy(item => item.CreatedDate)
                        .Where(item => item.Code > 3);

var result = query.Last();

Это требует сортировки всей коллекции, а затем фильтрации. Если бы у нас было миллион предметов, только один из которых имел код больше 3, мы бы потратили много времени на упорядочение результатов, которые были бы выброшены.

Сравните это с обратной обработкой, сначала фильтруя:

var query = myCollection.Where(item => item.Code > 3)
                        .OrderBy(item => item.CreatedDate);

var result = query.Last();

На этот раз мы заказываем только отфильтрованные результаты, которые в примере примера "только один элемент, соответствующий фильтру" будет намного более эффективным - как во времени, так и в пространстве.

Это также может повлиять на правильность выполнения запроса или нет. Рассмотрим:

var query = myCollection.Where(item => item.Code != 0)
                        .OrderBy(item => 10 / item.Code);

var result = query.Last();

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

Ответ 2

Да.

Но именно то, что эта разница в производительности зависит от того, как базовое дерево выражений оценивается поставщиком LINQ.

Например, ваш запрос может выполняться быстрее во второй раз (сначала с предложением WHERE) для LINQ-to-XML, но быстрее в первый раз для LINQ-to-SQL.

Чтобы точно определить разницу в производительности, вы, скорее всего, захотите профилировать свое приложение. Как всегда с такими вещами, преждевременная оптимизация обычно не стоит усилий - вы можете найти проблемы, отличные от производительности LINQ, более важные.

Ответ 3

В вашем конкретном примере это может повлиять на производительность.

Первый запрос: вашему вызову OrderBy необходимо выполнить итерацию по всей исходной последовательности, включая те элементы, где Code равно 3 или меньше. В предложении Where также необходимо выполнить итерацию всей упорядоченной последовательности.

Второй запрос: вызов Where ограничивает последовательность только теми элементами, где Code больше 3. Для вызова OrderBy требуется только пройти приведенную последовательность, возвращаемую вызовом Where.

Ответ 4

В Linq-To-Objects:

Сортировка выполняется довольно медленно и использует память O(n). Where, с другой стороны, относительно быстро и использует постоянную память. Поэтому выполнение Where сначала будет быстрее, а для больших коллекций значительно быстрее.

Уменьшенное давление памяти также может быть значительным, так как распределение на кучу большого объекта (вместе с их сборкой) относительно дорогое по моему опыту.

Ответ 5

Очевидно, что результаты должны быть одинаковыми...

Обратите внимание, что это на самом деле не так: в частности, следующие две строки будут давать разные результаты (для большинства поставщиков/наборов данных):

myCollection.OrderBy(o => o).Distinct();
myCollection.Distinct().OrderBy(o => o);

Ответ 6

Стоит отметить, что вы должны быть осторожны при рассмотрении how для оптимизации запроса LINQ. Например, если вы используете декларативную версию LINQ для выполнения следующих действий:

public class Record
{
    public string Name { get; set; }
    public double Score1 { get; set; }
    public double Score2 { get; set; }
}


var query = from record in Records
            order by ((record.Score1 + record.Score2) / 2) descending
            select new
                   {
                       Name = record.Name,
                       Average = ((record.Score1 + record.Score2) / 2)
                   };

Если по какой-то причине вы решили "оптимизировать" запрос, сначала сохраняя среднее значение в переменной, вы не получите желаемых результатов:

// The following two queries actually takes up more space and are slower
var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            order by average descending
            select new
                   {
                       Name = record.Name,
                       Average = average
                   };

var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            select new
                   {
                       Name = record.Name,
                       Average = average
                   }
            order by average descending;

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

Ответ 7

Это зависит от релевантности. Предположим, если у вас очень мало элементов с кодом = 3, то следующий порядок будет работать с небольшим набором коллекций, чтобы получить заказ по дате.

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

Итак, в обоих случаях будет разница в производительности