Сам компилятор C # не сильно изменяет выдаваемый IL в сборке Release.Следует отметить, что он больше не генерирует коды операций NOP, которые позволяют вам установить точку останова на фигурной скобке.Большой - это оптимизатор, который встроен в JIT-компилятор.Я знаю, что это делает следующие оптимизации:
Метод встраивания.Вызов метода заменяется введением кода метода.Это большое, оно делает методы доступа к свойствам практически свободными.
Распределение регистров процессора.Локальные переменные и аргументы метода могут храниться в регистре ЦП, и никогда (или реже) не сохраняться обратно во фрейм стека.Это большая проблема, которая отличается трудностью отладки оптимизированного кода.И придание ключевому слову volatile значения.
Исключение проверки индекса массива.Важная оптимизация при работе с массивами (все классы коллекций .NET используют массив внутри).Когда JIT-компилятор может проверить, что цикл никогда не индексирует массив вне границ, он устраняет проверку индекса.Большой.
Развертывание петли.Циклы с маленькими телами улучшаются, повторяя код до 4 раз в теле и зацикливаясь меньше.Снижает стоимость ветвления и улучшает возможности суперскалярного выполнения процессора.
Устранение мертвого кода.Утверждение вроде if (false) {/.../} полностью исключается.Это может произойти из-за постоянного складывания и наклона.В других случаях JIT-компилятор может определить, что у кода нет возможных побочных эффектов.Эта оптимизация делает код профилирования таким сложным.
Подъем кода.Код внутри цикла, на который цикл не влияет, может быть удален из цикла.Оптимизатор компилятора Си будет тратить гораздо больше времени на поиск возможностей для подъема.Тем не менее, это дорогостоящая оптимизация из-за необходимого анализа потока данных, и дрожание не может позволить себе время, поэтому поднимаются только очевидные случаи.Заставить программистов .NET писать лучший исходный код и поднять себя.
Устранение общих подвыражений.х = у + 4;z = y + 4;становится z = x;Довольно часто встречается в таких выражениях, как dest [ix + 1] = src [ix + 1];написано для удобства чтения без введения вспомогательной переменной.Не нужно ставить под угрозу удобочитаемость.
Постоянное сворачивание.х = 1 + 2;становится х = 3;Этот простой пример рано обнаруживается компилятором, но происходит во время JIT, когда другие оптимизации делают это возможным.
Копирование распространения.х = а;у = х;становится у = а;Это помогает распределителю регистра принимать лучшие решения.Это большая проблема в джиттере x86, потому что у него мало регистров для работы.Выбор правильных из них имеет решающее значение для перфектов.
Это очень важные оптимизации, которые могут иметь огромное значение , когда, например, вы профилируетеОтладьте сборку вашего приложения и сравните ее с версией выпуска.Это действительно имеет значение, хотя, когда код находится на вашем критическом пути, от 5 до 10% кода, который вы пишете, на самом деле , влияет на производительность вашей программы.Оптимизатор JIT не настолько умен, чтобы заранее знать, что критично, он может применять только «поворот на одиннадцать» для всего кода.
Эффективный результат этих оптимизаций для времени выполнения вашей программы часто зависит от кода, который выполняется в другом месте.Чтение файла, выполнение запроса к базе данных и т. Д. Выполнение работы оптимизатором JIT делает полностью незаметным.Хотя это не против:)
JIT-оптимизатор - это довольно надежный код, в основном потому, что он был проверен миллионы раз.Очень редко возникают проблемы в версии сборки вашей версии.Это случается однако.У xit и x86 jitters были проблемы со структурами.Джиттер x86 имеет проблемы с согласованностью с плавающей запятой, приводя к несколько иным результатам, когда промежуточные значения вычисления с плавающей запятой хранятся в регистре FPU с 80-битной точностью вместо усечения при сбросе в память.