Подсказки для оптимизации [закрытых] программ C#/.NET

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

В духе возвращения потерянного искусства, каковы некоторые подсказки, о которых Вы знаете для простого (или возможно комплекс) изменяется для оптимизации кода C#/.NET? Так как это - такая широкая вещь, которая зависит от того, что каждый пытается выполнить, это помогло бы предоставить контексту Вашу подсказку. Например:

  • Когда конкатенация многих строк вместе использует StringBuilder вместо этого. См. ссылку внизу для протестов на этом.
  • Использовать string.Compare сравнить две строки вместо того, чтобы делать что-то как string1.ToLower() == string2.ToLower()

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

Моя точка для того, чтобы даже отправить это должна иметь место для общих узких мест и как их можно избежать прежде даже столкнуться с ними. Это даже не обязательно о коде Plug and Play, которому любой должен слепо следовать, но больше о получении понимания, что о производительности нужно думать, по крайней мере несколько, и что существуют некоторые распространенные ошибки для внимательности.

Я вижу, хотя это, которое могло бы быть полезно также знать, почему подсказка полезна и где это должно быть применено. Для StringBuilder снабдите подсказкой я нашел справку, в которой я сделал давно здесь на сайте Jon Skeet.

78
задан 3 revs, 2 users 80% 5 September 2015 в 10:13
поделиться

10 ответов

  • Не используйте магические числа, используйте перечисления
  • Не жестко кодируйте значения
  • Используйте универсальные шаблоны там, где это возможно, поскольку это типично и позволяет избежать упаковки и распаковки
  • Используйте обработчик ошибок там, где это абсолютно необходимо
  • Утилизировать, утилизировать, утилизировать. CLR не знает, как закрыть соединения с базой данных, поэтому закройте их после использования и утилизируйте неуправляемые ресурсы
  • Используйте здравый смысл!
13
ответ дан 24 November 2019 в 10:24
поделиться

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

Vs. профилировщик, это дает вам стек вызовов и значения переменных, которые вы можете использовать, чтобы действительно понять, что происходит.

Сделайте это 10-20 раз, и вы получите хорошее представление о том, какая оптимизация действительно может иметь значение.

9
ответ дан 24 November 2019 в 10:24
поделиться

Истина в том, что идеального оптимизированного кода не существует. Однако вы можете оптимизировать для определенной части кода в известной системе (или наборе систем) на известном типе процессора (и количестве), известной платформе (Microsoft? Mono ?), Известная версия фреймворка / BCL , известная версия CLI, известная версия компилятора (ошибки, изменения спецификации, настройки), известный объем общей и доступной памяти, известное происхождение сборки ( GAC «диск? Удаленный?») С известной фоновой системной активностью со стороны других процессов.

В реальном мире используйте профилировщик и просмотрите важные биты; обычно очевидными вещами являются все, что связано с вводом-выводом, все, что связано с потоками (опять же, это сильно меняется между версиями), и все, что связано с циклами и поисками, но вы можете быть удивлены тем, что «явно плохой» код на самом деле не является проблемой, и то, что "заведомо хороший" код является огромным виновником.

6
ответ дан 24 November 2019 в 10:24
поделиться

Сообщать компилятору , что делать, а не , как это делать. Например, foreach (var item in list) лучше, чем для (int i = 0; i и m = list.Max (i => i.value); лучше, чем list.Sort (i => i.value); m = список [list.Count - 1]; .

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

В конечном итоге (и это относится ко всему программированию) минимизируйте циклы и минимизируйте то, что вы делаете в циклах. Еще важнее минимизировать количество петель внутри петель. В чем разница между алгоритмом O (n) и алгоритмом O (n ^ 2)? Алгоритм O (n ^ 2) имеет цикл внутри цикла.

5
ответ дан 24 November 2019 в 10:24
поделиться

Получите хороший профайлер.

Даже не пытайтесь оптимизировать C# (да и вообще любой код) без хорошего профилировщика. На самом деле, очень полезно иметь под рукой как выборочный, так и трассирующий профилировщик.

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

Первые три шага профилирования всегда должны быть следующими: 1) измерьте, 2) измерьте, а затем 3) измерьте....

45
ответ дан 24 November 2019 в 10:24
поделиться

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

2
ответ дан 24 November 2019 в 10:24
поделиться

Рекомендации по оптимизации:

  1. Не делайте этого, если в этом нет необходимости
  2. Не делайте этого, если дешевле бросить на решение проблемы новое оборудование вместо разработчика
  3. Не делайте этого, если не можете измерить изменения в производственной среде. эквивалентной среде
  4. Не делайте этого, если вы не знаете, как использовать CPU и профилировщик памяти
  5. Не делайте этого, если это сделает ваш код нечитаемым или не поддерживаемым

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

Начните с дальнего конца: используйте YSlow, чтобы понять, почему ваш веб-сайт работает медленно для конечных пользователей, затем вернитесь назад и исправьте обращения к базе данных, чтобы они были не слишком широкими (столбцы) и не слишком глубокими (строки).

В очень редких случаях, когда стоит сделать что-то для оптимизации использования процессора, будьте осторожны, чтобы не повлиять на использование памяти: Я видел "оптимизации", в которых разработчики пытались использовать память для кэширования результатов, чтобы сэкономить циклы процессора. Чистый эффект заключался в уменьшении доступной памяти для кэширования страниц и результатов базы данных, что делало работу приложения намного медленнее! (См. правило об измерении.)

Я также видел случаи, когда "тупой" неоптимизированный алгоритм побеждал "умный" оптимизированный алгоритм. Никогда не стоит недооценивать, насколько хорошо авторы компиляторов и разработчики микросхем научились превращать "неэффективный" циклический код в суперэффективный код, который может работать полностью в памяти кристалла с конвейеризацией. Ваш "умный" древовидный алгоритм с развёрнутым внутренним циклом, считающим в обратном направлении, который вы считали "эффективным", может быть побеждён просто потому, что он не смог остаться в памяти кристалла во время выполнения. (См. правило об измерении.)

.
21
ответ дан 24 November 2019 в 10:24
поделиться

У людей есть забавные представления о том, что на самом деле имеет значение. Stack Overflow полон вопросов, например, о том, является ли ++ i более «производительным», чем i ++ . Вот пример реальной настройки производительности , и это в основном та же процедура для любого языка. Если код просто написан определенным образом «потому что он быстрее», то это предположение.

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

8
ответ дан 24 November 2019 в 10:24
поделиться

Похоже, что оптимизация в наши дни - потерянное искусство.

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

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

То, что анализ производительности - это «потерянное искусство», это очень и очень хорошая вещь. Это искусство практиковалось как искусство . К оптимизации следует подходить так, какова она есть: инженерная проблема , которую можно решить путем тщательного применения твердых инженерных принципов.

За эти годы меня десятки раз спрашивали о моем списке «советов и приемов», которые люди могут использовать для оптимизации своего vbscript / jscript / активных страниц сервера / VB / кода C #. Я всегда этому сопротивляюсь. Упор на «советы и рекомендации» - это совершенно неправильный подход к производительности. Таким образом, получается код, который трудно понять, сложно рассуждать, трудно поддерживать, который обычно не намного быстрее, чем соответствующий простой код.

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

  • Ставить значимые, измеримые, ориентированные на клиента цели.
  • Создавайте наборы тестов, чтобы проверить свою производительность в соответствии с этими целями в реалистичных, но контролируемых и повторяемых условиях.
  • Если эти комплекты показывают, что вы не достигаете своих целей, воспользуйтесь такими инструментами, как профилировщики, чтобы выяснить, почему.
  • Оптимизируйте то, что профилировщик определяет как наихудшую подсистему. Продолжайте профилировать каждое изменение, чтобы четко понимать влияние каждого изменения на производительность.
  • Повторяйте до тех пор, пока не произойдет одно из трех: (1) вы достигнете своих целей и отправите программное обеспечение, (2) вы пересмотрите свои цели в сторону уменьшения до того, что вы можете достичь, или (3) ваш проект будет отменен, потому что вы не смогли выполнить свои цели.

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

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

106
ответ дан 24 November 2019 в 10:24
поделиться

При работе с ORMs помните о N+1 Selects.

List<Order> _orders = _repository.GetOrders(DateTime.Now);
foreach(var order in _orders)
{
    Print(order.Customer.Name);
}

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

16
ответ дан 24 November 2019 в 10:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: