Является ли .NET CLR JIT компилировать каждый метод каждый раз?

Я знаю, что Java HotSpot JIT иногда пропускает JIT, компилируя метод, если ожидает, что накладные расходы компиляции будут ниже, чем накладные расходы на запуск метода в интерпретируемом режиме. Является ли .NET CLR работать на основе аналогичной эвристики?

Ответ 1

Примечание: этот ответ находится в контексте "за запуск". Обычно код запускается JIT при каждом запуске программы. Используя ngen или .NET Native также изменяет эту историю...

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

Это может измениться, конечно, но так было, так как v1 и я не ожидаю, что он скоро изменится.

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

Одним из преимуществ использования .NET является то, что большинство языков CLR делают методы не виртуальными по умолчанию, что означает, что может быть сделано гораздо больше инкрустаций. HotSpot может встроить метод, пока он не будет переопределен, и в какой момент он отменяет оптимизацию (или некоторые умные вещи в некоторых случаях условно продолжают использовать встроенный код на основе фактического типа). С меньшим количеством виртуальных методов, о которых нужно беспокоиться,.NET может в значительной степени игнорировать боль, когда вы не можете внедрить что-либо виртуальное.

EDIT: вышеописанная структура рабочего стола. Compact Framework выдает собственный код, когда он хочет, JITting снова по мере необходимости. Однако это все еще не похоже на адаптивную оптимизацию HotSpots.

Микроархитектура не JIT, по-видимому, интерпретирует код. Это имеет смысл для очень ограниченных устройств. (Я не могу сказать, что я много знаю о микро-каркасе.)

Ответ 2

Рабочая среда .NET всегда компилирует код JIT перед выполнением. Поэтому он никогда не интерпретируется.

Вы можете найти более интересное чтение в CLR Design Choices с Андерс Хейлсберг. Особенно часть:

Я читал, что Microsoft решила, что IL всегда будет скомпилирован, никогда не будет интерпретироваться. Как информация типа кодирования в инструкциях помогает интерпретаторам работать более эффективно?

Андерс Хейлсберг: Если интерпретатор может просто слепо делать то, что говорят инструкции, не требуя отслеживания того, что находится в верхней части стека, он может идти быстрее. Например, когда он видит iadd, интерпретатор не должен сначала выяснить, какой тип его добавить, он знает его как целое. Предполагая, что кто-то уже проверял, что стек выглядит правильно, безопасно сократить время там, и вы заботитесь об этом для переводчика. Однако в нашем случае мы никогда не планировали нацеливать интерпретируемый сценарий на CLR. Мы намеревались всегда использовать JIT [Just-in-time compile], и ​​для целей JIT нам нужно было отслеживать информацию о типе. Поскольку у нас уже есть информация о типе, он фактически не покупает нам ничего, чтобы поместить его в инструкции.

Bill Venners: Многие современные JVM [виртуальные машины Java] выполняют адаптивную оптимизацию, где они начинают с интерпретации байт-кодов. Они просматривают приложение, когда он работает, чтобы найти от 10% до 20% кода, который выполняется от 80% до 90% времени, а затем они скомпилируют это для native. Тем не менее, они не обязательно точно компилируют эти байткоды. Интерпретатор метода все еще может быть выполнен интерпретатором, поскольку он компилируется в native и оптимизируется в фоновом режиме. Когда родной код готов, он может заменить байт-коды. Не нацеливаясь на интерпретируемый сценарий, полностью ли вы исключили этот подход к выполнению в среде CLR?

Андерс Хейлсберг: Нет, мы не полностью это приняли. Мы все еще можем интерпретировать. Мы просто не оптимизированы для интерпретации. Мы не оптимизированы для написания этого интерпретатора с наивысшей производительностью, который будет когда-либо интерпретировать. Я не думаю, что кто-то это делает. Для телеприставки 10 лет назад это могло быть интересно. Но это уже не интересно. Технологии JIT дошли до того, что вы можете иметь несколько возможных стратегий JIT. Вы даже можете себе представить, используя быстрый JIT, который быстро разрывается, а затем, когда мы обнаруживаем, что мы все время выполняем определенный метод, используя другой JIT, который проводит немного больше времени и делает лучшую работу по оптимизации. Там гораздо больше можно сделать JIT-мудрый.

Ответ 3

В будущем будет полезно увидеть некоторые JIT на основе трассировки для устройств с низкой памятью. Это будет в основном интерпретировать, находить горячие точки и преобразовывать их в ассемблер и кэшировать их. Я думаю, что это то, что Google делает со своими Android JIT и Microsoft Research имеет исследовательский проект, продолжающийся для JIT на основе трассировки.

Я нашел статью, SPUR: JIT-компилятор на основе трассировки для CIL. Возможно, некоторые из них превратят его в CLR в один прекрасный день?

Ответ 4

Я так не верю, и я не думаю, что это когда-либо должно.

Как JIT может знать, сколько раз будет вызываться конкретный метод? Не повлияет ли частота интерпретации фактора на решение?

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