Как лучше всего оптимизировать или «настроить» выражения LINQ? - PullRequest
3 голосов
/ 18 марта 2009

При построении выражений LINQ (для меня linq to objects) есть много способов сделать что-то, некоторые намного, намного лучше и эффективнее, чем другие.

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

EDIT

Спасибо, Ричард / Джон, за ваши ответы.

Мне кажется, что я действительно хочу получить способ подсчета операций "OCount" для выражения LINQ, хотя я не уверен, что в LINQ существуют зацепки, позволяющие это сделать. Предположим, что у меня есть целевой уровень производительности для конкретного оборудования (SLA). В идеале я хотел бы добавить модульный тест, чтобы подтвердить, что типичные данные, перемещаемые по этому запросу, будут обрабатываться в течение указанного времени (из SLA). Проблема в том, что это будет выполнено на сервере сборки / машине разработчиков / и т.д. что, вероятно, имеет мало сходства с аппаратным обеспечением SLA. Таким образом, идея заключается в том, что я бы определил приемлемый максимальный «OCount» для выражения, зная, что если OCount меньше X, он, безусловно, обеспечит приемлемую производительность в соответствии с SLA на целевом «типичном» оборудовании. Если OCount превышает этот порог, сборка / модульный тест выдаст предупреждение. В идеале я хотел бы иметь что-то вроде этого (псевдокод-иш):

var results = [big linq expression run against test dataset];
Assert.IsLess(MAXALLOWABLE_OCOUNT, results.OCount)

, где results.OCount просто дал бы мне общее количество итераций (n), необходимое для получения набора результатов.

Зачем мне это нравится ??

Что ж, даже при выражении LINQ небольшого размера небольшое изменение / добавление может оказать ОГРОМНОЕ влияние на производительность вследствие увеличения общего количества операций. Код приложения будет по-прежнему проходить все модульные тесты, так как он все равно будет давать правильный результат, но при развертывании будет работать очень медленно.

Другая причина - простое обучение. Если вы что-то делаете и счет увеличивается на порядок, то вы чему-то учитесь.

РЕДАКТИРОВАТЬ # 2 Я также добавлю потенциальный ответ. Это не мое, оно взято из Кэмерона МакФарланда из другого вопроса, который я задал, который породил этот. Оказывается, я думаю, что ответ на этот вопрос мог бы работать здесь в среде модульного тестирования, подобной той, которую я описал в первом редактировании этого вопроса.

Суть его заключается в создании наборов тестовых данных в модульном тестовом приборе, который вы вводите в выражение LINQ, как описано в этом ответе, а затем суммируете счетчики итераций и сравниваете их с максимально допустимым числом итераций. 1034 *

См. ответ Кэмерон здесь

Ответы [ 3 ]

6 голосов
/ 18 марта 2009

В основном вам нужно выработать функцию сложности. Это зависит от оператора, но, к сожалению, не очень хорошо документировано.

(По общему принципу я согласен с ответом Ричарда - это всего лишь LINQ to Objects.)

Если у вас есть конкретные операторы, которые вас интересуют, стоило бы спросить о них, но не в моей голове:

  • Выбор = O (n)
  • Где = O (n)
  • Join = O (внутренние + внешние + совпадения) (т.е. это не дешевле, чем inner + outer, но может быть таким же плохим, как inner * outer в зависимости от результатов)
  • GroupJoin = то же самое, что и Join, но буферизуется вместо потоковой передачи по внешнему
  • OrderBy = O (n log n)
  • SelectMany = O (n + результаты)
  • Количество = O (1) или O (n) в зависимости от того, реализует ли он IList
  • Количество (предикат) = O (n)
  • Макс / Мин = O (n)
  • Все / Любые = O (n) (возможно раннее завершение)
  • Distinct = O (n)
  • Skip / Take = O (n)
  • SkipWhile / TakeWhile = O (n)

Точные характеристики зависят от того, буферизует оператор или от потоков.

3 голосов
/ 18 марта 2009
  1. Получить SLA (или другое определение) с описанием требуемой общей производительности.

  2. Измерьте производительность приложений и насколько это ниже требований (если в рамках требований остановитесь и сделайте что-нибудь полезное).

  3. Используйте профилировщик, чтобы получить подробную разбивку по производительности, определить части системы, которые наиболее могут быть улучшены (небольшое улучшение для горячего кода, вероятно, будет лучше, чем большое улучшение для редко называемого кода).

  4. Внести изменения, перезапустить модульные / функциональные тесты (нет смысла делать неправильные вещи быстро).

  5. Перейти к 1.

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

0 голосов
/ 18 марта 2009

Добавление на Джона, который добавляет на Ричарда

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

Например, вызов MoveNext () в следующих операциях LINQ будет обрабатывать один результат за раз

  • Выберите
  • Где

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

  • OrderBy
  • Кроме (полностью обрабатывает другую коллекцию)
...