среда, 26 марта 2014 г.

Заметки о производительности для .NET приложений



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

Меньше исключений

Бросание исключения может быть чрезвычайно дорогим, поэтому следует очень аккуратно  пользоваться данным механизмом. Также рекомендуется использовать утилиту Perfmon для анализа количества и причин бросаемых исключений в рамках отдельно взятого приложения. Результаты порой могут удивить большим количеством исключений, бросаемых отдельными частями приложения. Также для анализа данной области можно испоьзовать программные методы, такие как Performance Counters в рамках вашего приложения.
 

Используйте вызовы полноценных методов

Вызоыв полноценных методов (chunky calls) способствуют тому что за один вызов выполняется некоторая завершенная последовательность дейсвтий, например инициализация нескольких полей объекта. В противовес этому сущесвует понятние вызова раздробленных методов (chatty calls) – которые выполняют единичные атомарные операции за вызов и требуют определенного количества вызовов таких методов для достижения аналогичного результата (примером раздробленных методов может быть множество методов каждый из которых инициализирует только одно поле объекта ). Очень важно использовать как можно больше полноценных методов и избавлятся от раздробленных, так как ресурсные затраты на выполнения каждого метода несоизмеримо выше их простоты и лакончиности. P/Invoke, interop и удаленные вызовы все имеют огромные ресурсные затраты и поэтому такие вызовы следует использовать  как можно реже. В каждом из данных случаев, следует спроектировать приложение таким образом,  что бы оно вовсе не имело вызовов раздробленных методов.

Проектируйте с ValueType

Используйте структуры для хранения данных – это позволит избежать дополнительных накладных расходов на упакову/раcпаковку (boxing/unboxing)

Используйте метод AddRange для добавления групп элементов

Используйте метод AddRange для добавления группы элментов целиком, а не поодиночке каждый элемент в отдельности в цикле. Практически все элемнеты управления и коллекции имеют в в своем арсенале оба метода Add и AddRange, и каждый из них оптимизирован для своих задач. Add полезен для добавления одного элемента, в то время как AddRage имеет некоторые накладные расходы, но ведет себя лучше в случае добавления группы элементов. 

Уменьшайте количество используемых сборок

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

Используйте цикл For для итерации внутри строк

В C# имеется чрезвычайно удобный инструмент foreach, который позволяет проходить по колекции, строке по каждому элменту и выполнять над ним некоторые действия. Это очень хороший инструмент так как предоставляет способ перечисления элементов любого типа. Недостатоком является его скорость, и если вам приходится в цикле обращатся к символам строки лучше использовать for для таких целей. Строки это обыкновенные массивы символов, поэтому накладные расходы при их перечислении минимальны. JIT достаточно умен (в большинстве случаев) и умеет оптимизировать все необходимые действия выполняемые при перечислении с помощью for, но не в случае с foreach. Как результат for в пять раз быстрее чем foreach (по крайней мере в ранних версиях .NET). В нынешних версиях ситуация стала получше, однако этот факт все стоит даржать в голове, особенно когда приходится работать c Legacy кодом.

Используйте StringBuilder для сложных манипуляций со строками

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

Делайте прекомпиляцию приложений Windows Forms

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

Используйте массивы массивов

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

Устанавливайте размер буфера в диапазоне от 4Кб до 8Кб

Практически для любых приложений, данный размер буфера дает максимум производительности. Для очень специфических случаев вероятно может потребоваться другой размер, например при загрузке большого изображения, с фиксированным размером. Но в 99% случаев – больший размер буфера – это просто пустая трата памяти. Все объекты наследуемые от BufferStream позволяют уставновить такой размер который нужен вам, и в абсолютнном большинстве случаем оптимальным размером будет от 4 до 8.

Будте бдительны при использовании  асинхронных операции ввода вывода

В некоторых случаях, вам может понадобится использование асинхронных оперций ввода вывода. Например, вам может понадобится загружать и распаковывать  серию файлов: вы можете читать данные с одного потока, рапаковывать их на процессоре и после рапаковки записывать результат в другой поток. Использование асинхронных операций требует определенного умения, и в случаях некорректого использования вы можете столкнутся с падением производительности, но если эти оперции использовать правильно вы может выиграть занчительно больше.
 
В основу статьи положена публикация http://msdn.microsoft.com/en-us/library/ms973839.aspx