Важен ли порядок предиката при использовании LINQ? - PullRequest
8 голосов
/ 24 февраля 2012

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

List<TestItem> slowResults = items.OrderBy(item => item.StringItem)
                                  .Where(item => item.IntItem == 100)
                                  .ToList();

и это быстрее:

List<TestItem> fastResults = items.Where(item => item.IntItem == 100)
                                  .OrderBy(item => item.StringItem)
                                  .ToList();

Но это не мой вопрос:

Мой вопрос касается производительности короткого замыкания в связи с предикатом LINQ. Когда я использую предложение Where, как в этом случае:

List<TestItem> results = items.Where(item => item.Item1 == 12 &&
                                             item.Item2 != null &&
                                             item.Item2.SubItem == 65 &&
                                             item.Item3.Equals(anotherThingy))
                              .ToList();

Разве порядок аргументов не имеет значения? Например, я ожидаю, что сначала выполнение .Equals приведет к более медленному запросу в целом, поскольку целочисленная оценка Item1 == 12 является гораздо более быстрой операцией?

Если порядок имеет значение, то насколько это важно? Конечно, вызов таких методов, как .Equals, вероятно, приводит к гораздо большему замедлению, чем если бы я только сравнивал несколько целых чисел, но это относительно небольшое снижение производительности по сравнению с тем, как работает «медленный» LINQ? Поскольку LINQ делает тонны вызовов методов, действительно ли что-то вроде .Equals действительно будет иметь значение, поскольку - если оно не будет переопределено - оно будет выполнять собственный код фреймворка, верно? С другой стороны, вызов стандартного метода MSIL будет значительно медленнее?

Кроме того, есть ли другие оптимизации компилятора в этом запросе, которые могли бы ускорить это под капотом?

Спасибо за мысли и разъяснения! Brett

1 Ответ

13 голосов
/ 25 февраля 2012

Ответ будет разным для разных поставщиков LINQ.В частности, история очень отличается для LINQ to Objects и, скажем, LINQ to Entities.

В LINQ to Object оператор Where принимает фильтр как Func,Func <,> является делегатом, поэтому для целей этого обсуждения вы можете рассматривать его как указатель на функцию.В LINQ to Objects ваш запрос эквивалентен следующему:

static void Main() {
    List<TestItem> results = items.Where(MyFilter).ToList(); 

static boolean MyFilter(TestItem item) {
    return item.Item1 == 12 && 
        item.Item2 != null && 
        item.Item2.SubItem == 65 && 
        item.Item3.Equals(anotherThingy)
}

Главное, на что нужно обратить внимание, это то, что MyFilter - это обычный метод C #, поэтому применяются обычные правила C #, включая поведение короткого замыкания &&.Следовательно, условия будут оцениваться в том порядке, в котором вы их написали.LINQ to Objects может вызывать MyFilter для разных входных элементов, но не может изменить то, что делает MyFilter.

В LINQ to Entities и LINQ to SQL оператор Where принимает фильтр как выражение> .Теперь фильтр передается в оператор Where как структура данных, которая описывает выражение.В этом случае поставщик LINQ будет смотреть на структуру данных («дерево выражений»), и поставщик LINQ должен решить, как его интерпретировать.

В случаях LINQ to Entities и LINQ to SQL, дерево выражений будет переведено в SQL.Затем сервер баз данных решает, как выполнить запрос.Серверу определенно разрешено изменять порядок условий, и это может привести к еще более существенной оптимизации.Например, если таблица SQL содержит индекс по одному из столбцов, указанных в условии, сервер может выбрать использование индекса и даже не просматривать строки, которые не соответствуют этой конкретной части условия.

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