Подтвердить что ты не робот

Проблемы с производительностью при использовании LINQ "везде"?

После обновления до ReSharper5 он дает мне еще более полезные советы по улучшению кода. Один из них, который я вижу повсюду, теперь является подсказкой для замены foreach-операторов запросами LINQ. Возьмите этот пример:

private Ninja FindNinjaById(int ninjaId)
{
    foreach (var ninja in Ninjas)
    {
        if (ninja.Id == ninjaId)
            return ninja;
    }
    return null;
}

Это предложение заменено следующим использованием LINQ:

private Ninja FindNinjaById(int ninjaId)
{
    return Ninjas.FirstOrDefault(ninja => ninja.Id == ninjaId);
}

Все выглядит хорошо, и я уверен, что нет никакой проблемы с производительностью, чтобы заменить этот foreach. Но это что-то, что я должен делать в целом? Или я могу столкнуться с проблемами производительности со всеми этими запросами LINQ?

4b9b3361

Ответ 1

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

Ответ 2

Позвольте мне начать с того, что я люблю LINQ для его выразительности и постоянно использую его без каких-либо проблем.

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

Вот набор различий, о которых вы должны знать, что может иметь значение с производительностью:

  • LINQ чрезмерно использует делегированные вызовы, а делегировать вызовы (очень маленький) медленнее, чем вызовы методов и, конечно, медленнее, чем встроенный код.
  • Делегат - это указатель на метод внутри объекта. Этот объект необходимо создать. Операторы LINQ обычно возвращают новый объект (итератор), который позволяет осуществлять цикл через коллекцию. Таким образом, связанные операторы LINQ создают несколько новых объектов.
  • Когда ваш внутренний цикл использует объекты извне (называемые замыканиями), они также должны быть обернуты в объекты (которые необходимо создать).
  • Многие операторы LINQ вызывают метод GetEnumerator в коллекции для его итерации. Вызов GetEnumerator обычно обеспечивает создание еще одного объекта.
  • Итерирование коллекции выполняется с использованием интерфейса IEnumerator. Интерфейсные вызовы немного медленнее, чем обычные вызовы методов.
  • IEnumerator объекты часто должны быть удалены или, по крайней мере, Dispose должен быть вызван.

Когда производительность вызывает беспокойство, попробуйте использовать for над foreach.

Опять же, мне нравится LINQ и Я не могу вспомнить, что когда-либо решил не использовать запрос LINQ (для объектов) из-за производительности. Таким образом, не делает преждевременной оптимизации. Начните сначала с самого читаемости, а затем оптимизируйте, когда это необходимо. Профиль профиля, профиля и .

Ответ 3

Профиль


Единственный способ узнать наверняка - это профиль. Да, некоторые запросы могут быть медленнее. Но когда вы смотрите на то, что здесь заменил ReSharper, это, по сути, то же самое, что сделано по-другому. Ниндзя зациклированы, каждый идентификатор проверяется. Во всяком случае, вы можете утверждать, что этот рефакторинг сводится к удобочитаемости. Какой из двух вам легче читать?

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

Ответ 4

Мы построили массивные приложения, LINQ посыпался либерально. Это никогда не замедляло нас.

Совершенно возможно писать запросы LINQ, которые будут очень медленными, но проще исправить простые операторы LINQ, чем огромные для алгоритмов /if/for/return.

Возьмите консультацию о помощи:)

Ответ 5

Одна вещь, которую мы определили как проблему производительности, - это создание множества лямбда и повторение небольших коллекций. Что происходит в преобразованном образце?

Ninjas.FirstOrDefault(ninja => ninja.Id == ninjaId)

Сначала создается новый экземпляр (сгенерированного) типа закрытия. Новый экземпляр в управляемой куче, некоторые работают для GC. Во-вторых, новый экземпляр делегата создается из метода в этом закрытии. Затем вызывается метод FirstOrDefault. Что оно делает? Он выполняет итерацию коллекции (так же, как и исходный код), и вызывает делегат.

Итак, у вас есть 4 вещи: 1. Создать закрытие 2. Создать делегат 3. Вызовите делегата 4. Соберите закрытие и делегируйте

Если вы назовете FindNinjaById много раз, вы добавите это, чтобы быть важным perforamnce hit. Конечно, измерьте его.

Если вы замените его (эквивалентным)

Ninjas.Where(ninja => ninja.Id == ninjaId).FirstOrDefault()

добавляет 5. Создание конечного автомата для итератора ( "Где" дает функцию)

Ответ 6

Анекдот: когда я только познакомился с С# 3.0 и LINQ, я все еще был в своем "когда у вас есть молот, все выглядит как гвоздь". Как школьное задание, я должен был написать соединить четыре/четыре в игре ряда как упражнение в алгоритмах поиска состязаний. Я использовал LINQ всюду по программе. В одном конкретном случае мне нужно было найти строку, в которую играла бы игра, если бы я уронил ее в определенном столбце. Идеальный прецедент для запроса LINQ! Это оказалось очень медленным. Однако LINQ не была проблемой, проблема заключалась в том, что я искал для начала. Я оптимизировал это, просто сохраняя таблицу поиска: целочисленный массив, содержащий номер строки для каждого столбца игрового поля, обновляющий эту таблицу при вставке игрового фрагмента. Излишне говорить, что это было намного, намного быстрее.

Извлеченный урок: сначала оптимизируйте свой алгоритм, а конструкции на высоком уровне, такие как LINQ, могут сделать это проще.

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

Ответ 7

Вышеупомянутое делает то же самое.

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

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

Ответ 8

Самое интересное в запросах LINQ заключается в том, что он делает dead simple для преобразования в параллельный запрос. В зависимости от того, что вы делаете, это может быть или не быть быстрее (как всегда, профиль), но тем не менее он довольно опрятен.

Ответ 9

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

Вы "инвалид" на iPhone 3GS примерно до 46 МБ оперативной памяти и 620-мегагерцовый ARM-процессор. По общему признанию, код AOT скомпилирован, но даже на симуляторе, где он находится JIT'd и проходит длинную серию косвенных изменений, разница составляет десятые доли миллисекунды для множеств из 1000 объектов.

Наряду с Windows Mobile вы должны будете беспокоиться о затратах на производительность, а не в огромных приложениях ASP.NET, работающих на четырехъядерных 8-гигабайтных серверах или настольных компьютерах с двойным счетом. Единственное исключение из этого - с большими наборами объектов, хотя, возможно, в любом случае вы будете ленивой загрузкой, а исходная задача запроса будет выполняться на сервере базы данных.

Это немного клише на Stackoverflow, но используйте более короткий читаемый код до 100 секунд миллисекунд действительно имеет значение.