Различия в производительности между сборками debug и release

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

насколько я знаю, единственное различие между этими конфигурациями, если вы не измените его вручную, это Debug есть DEBUG постоянная определено, и релиз есть оптимизировать код проверка.

Итак, мои вопросы на самом деле двоякая:

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

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

8 ответов


сам компилятор C# не сильно изменяет испускаемый IL в сборке выпуска. Примечательно, что он больше не излучает опкоды NOP, которые позволяют установить точку останова на фигурной скобке. Большой-это оптимизатор, встроенный в JIT-компилятор. Я знаю, что он делает следующие оптимизации:

  • способ подстановки. Вызов метода заменяется введением кода метода. Это большой, он делает свойства accessors по существу бесплатный.

  • выделение регистра процессора. Локальные переменные и аргументы метода могут храниться в регистре ЦП без (или реже) сохранения обратно в кадр стека. Это большой, примечательный тем, что делает отладку оптимизированного кода настолько сложной. И давая летучие ключевое слово a значение.

  • исключение проверки индекса массива. Важная оптимизация при работе с массивами (все классы .NET collection используют массив внутри). Когда компилятор JIT может проверить, что цикл никогда не индексирует массив за пределами границ, он устранит проверку индекса. Верзила.

  • развертывание циклов. Петли с небольшими телами улучшаются путем повторения кода до 4 раз в теле и зацикливания меньше. Уменьшает стоимость ветви и улучшает супер-скалярные параметры выполнения процессора.

  • исключение "мертвых" код. Утверждение типа if (false) { /.../} полностью устраняется. Это может произойти из-за постоянного складывания и встраивание. В других случаях компилятор JIT может определить, что код не имеет возможного побочного эффекта. Эта оптимизация делает код профилирования таким сложным.

  • поднимая код. Код внутри цикла, который не зависит от цикла, может быть перемещен из цикла. Оптимизатор компилятора C потратит гораздо больше времени на поиск возможностей для подъема. Это однако дорогостоящая оптимизация из-за необходимого анализа потока данных и дрожания не может позволить себе время, поэтому только поднимает очевидные случаи. Заставляя программистов .NET писать лучший исходный код и поднимать себя.

  • исключение общего под-выражения. x = y + 4; z = y + 4; становится z = x; довольно распространено в таких утверждениях, как dest[ix+1] = src[ix+1]; написано для удобства чтения без введения вспомогательной переменной. Не нужно идти на компромисс. удобочитаемость.

  • постоянный складной. x = 1 + 2; становится x = 3; Этот простой пример рано пойман компилятором, но происходит в JIT время, когда другие оптимизации делают это возможным.

  • распространение копию. x = a; y = x; становится y = a; это помогает распределителю регистров принимать лучшие решения. Это большое дело в дрожании x86, потому что у него мало регистров для работы. Имея его выбрать правильные из них имеет решающее значение для перфорация.

Это очень важные оптимизации, которые могут сделать большой разница, когда, например, вы профилируете отладочную сборку своего приложения и сравниваете ее со сборкой выпуска. Это имеет значение только тогда, когда код находится на вашем критическом пути, от 5 до 10% кода Вы пишете, что на самом деле влияет на perf вашей программы. Оптимизатор JIT недостаточно умен, чтобы знать заранее, что важно, он может применяться только "повернуть на одиннадцать" наберите весь код.

эффективный результат этих оптимизаций на время выполнения программы часто зависит от кода, который выполняется в другом месте. Чтение файла, выполнение запроса dbase и т. д. Делая работу JIT optimizer делает полностью невидимым. Это не возражает, хотя:)

оптимизатор JIT-довольно надежный код, в основном потому, что он был протестирован миллионы раз. Крайне редко возникают проблемы в Отпустите версию сборки вашей программы. Однако это случается. Как x64, так и x86 jitters имели проблемы со структурами. Дрожание x86 имеет проблемы с согласованностью с плавающей запятой, производя тонко разные результаты, когда промежуточные элементы вычисления с плавающей запятой хранятся в регистре FPU с 80-битной точностью, а не усекаются при сбросе в память.


  1. Да, есть много различий в производительности, и они действительно применяются по всему вашему коду. Debug делает очень мало оптимизации производительности, и режим выпуска очень много;

  2. только код, который полагается на DEBUG константа может работать по-разному с построения выпуска. Кроме того, вы не должны видеть никаких проблем.

пример кода фреймворка, который зависит от DEBUG постоянный Debug.Assert() метод, который атрибут [Conditional("DEBUG)"] определенными. Это означает, что это также зависит от DEBUG константа, и это не включено в сборку выпуска.


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

однако, если у вас есть много вычислений-тяжелых вычислений, то вы заметите различия (может быть до 40%, как упоминал @Pieter, хотя это будет зависеть от характера расчетов).

Это в основном компромисс дизайна. Если вы выпускаете в DEBUG build, то, если пользователи испытывают проблемы, вы можете получить более значимую обратную трассировку и сделать гораздо более гибкую диагностику. Выпуская отладочную сборку, вы также избегаете создания оптимизатора obscure Heisenbugs.


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

  • одна вещь, которая может укусить вас с Release builds, заключается в том, что код сборки отладки иногда может подавлять условия гонки и другие ошибки, связанные с потоками. Оптимизированный код может привести к переупорядочению команд, а более быстрое выполнение может усугубить определенную гонку условия.


вы никогда не должны выпускать сборку отладки .NET в производство. Он может содержать уродливый код для поддержки редактирования и продолжения или кто знает, что еще. Насколько я знаю, это происходит только в VB, а не C# (Примечание: исходный пост помечен C#), но это все равно должно дать повод сделать паузу относительно того, что Microsoft думает, что им разрешено делать с отладочной сборкой. Фактически, до .NET 4.0 код VB пропускает память пропорционально количеству экземпляров объектов с событиями, которые вы создаете в поддержку Edit-and-Continue. (Хотя это, как сообщается, исправлено в https://connect.microsoft.com/VisualStudio/feedback/details/481671/vb-classes-with-events-are-not-garbage-collected-when-debugging, сгенерированный код выглядит неприятно, создавая WeakReference объекты и добавление их в статический список в то время как блокировка) Я конечно не хочу такой поддержки отладки в рабочей среде!


по моему опыту, худшее, что вышло из режима выпуска, - это неясные "ошибки выпуска". Поскольку IL (промежуточный язык) оптимизирован в режиме выпуска, существует возможность ошибок, которые не проявились бы в режиме отладки. Есть другие вопросы SO, охватывающие эту проблему: общие причины ошибок в версии выпуска отсутствуют в режиме отладки

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


Я бы сказал, что 1) во многом зависит от вашей реализации. Обычно разница не так уж велика. Я сделал много измерений и часто не видел разницы. Если использовать неуправляемый код, множество огромных массивов и тому подобное, разница в производительности немного больше, но не в другой мир (как в C++). 2) Обычно в коде выпуска отображается меньше ошибок (более высокий допуск), поэтому коммутатор должен работать нормально.


    **Debug Mode:**
    Developer use debug mode for debugging the web application on live/local server. Debug mode allow developers to break the execution of program using interrupt 3 and step through the code. Debug mode has below features:
   1) Less optimized code
   2) Some additional instructions are added to enable the developer to set a breakpoint on every source code line.
   3) More memory is used by the source code at runtime.
   4) Scripts & images downloaded by webresource.axd are not cached.
   5) It has big size, and runs slower.

    **Release Mode:**
    Developer use release mode for final deployment of source code on live server. Release mode dlls contain optimized code and it is for customers. Release mode has below features:
    More optimized code
    Some additional instructions are removed and developer can’t set a breakpoint on every source code line.
   1) Less memory is used by the source code at runtime.
   2) Scripts & images downloaded by webresource.axd are cached.
   3) It has small size, and runs fast.
   4) Scripts & images downloaded by webresource.axd are cached.
   5) It has small size, and runs fast.