БлогеРОК программиста-барабанщика 👨💻🥁
Telegram: @GranSteL
YouTube: https://youtube.com/@granstel
ВКВидео: https://vkvideo.ru/@drummer_programmer
Post #542
66
Forwarded from C# Short Posts 🔞

😎Tiered compilation: почему JIT компилирует метод минимум дважды
В прошлый раз мы увидели, как JIT переводит IL в машинный код процессора🤩
Там я упомянул, что обычно метод сначала компилируется «наспех», чтобы он вообще работал. А если метод окажется горячим🌚 , то позже он компилируется заново, уже с оптимизациями📈
Разберёмся, почему так, что такое горячий метод, и что за оптимизации👇
🎯 Дилемма: быстрый старт или быстрый код
У JIT конфликт интересов: по-хорошему, надо бы оптимизировать весь код, чтобы он был очень быстрым и эффективным. Но оптимизация — дело небыстрое. Если возиться с каждым методом при первом же вызове, программа будет мучительно долго стартовать 🐢
При этом некоторые методы вызываются лишь несколько раз за время выполнения программы, так что тратить время на их оптимизацию бессмысленно. А те, что вызываются постоянно, наоборот, хочется сделать супер-быстрыми 🔥
2️⃣ Решение — два уровня компиляции
🟢 Tier0 — быстрый перевод без оптимизаций, чтобы метод поскорее заработал
🟢 Tier1 — компиляция с полным набором оптимизаций. Применяется только к тем методам, которые вызываются достаточно часто
🧰 Какие именно оптимизации?
🟢 Встраивание: тело короткого метода подставляется прямо в место вызова. Экономим на переходе к нему и открываем дорогу другим оптимизациям
🟢 Раскладка по регистрам: нужные значения переменных, аргументов, и промежуточных вычислений держатся в регистрах процессора — самой быстрой памяти, а не гоняются в оперативную память
🟢 Разворачивание циклов: короткий цикл превращается в несколько дублирований тела цикла, чтобы реже проверять условие выхода
🟢 Выкидывание лишнего: вычисления, результат которых никто не читает, просто убираются
♨️ Как JIT понимает, что метод горячий
У каждого метода есть счётчик вызовов. Пока его значение небольшое, вызывается та версия метода, которая была скомпилирована JIT при первом вызове, то есть на уровне Tier0. Как только счётчик переваливает за определённый порог, к методу в фоне применяется Tier1, и следующие вызовы идут уже к оптимизированному методу⚡️
По умолчанию этот порог составляет 30 вызовов (
До 30 вызовов включительно метод так и остаётся скомпилированным на Tier0. На 31-м же видно, что метод скомпилировался заново, уже на Tier1 ✨
🅰️ Что запомнить
🟢 Первые вызовы горячего метода идут по версии, скомпилированной на Tier0, то есть без особых оптимизаций
🟢 Если метод вызывается достаточно часто (по умолчанию, больше 30 раз), то он компилируется на Tier1, с оптимизациями
🤯 Вот это поворот
Заглавный дикпик заспойлерил, что на самом деле уровней больше😱
Ну или не прям уровней, а разновидностей Tier0 и Tier1, но это уже разберём в следующий раз 👉
🧑💻dp🥁
#dotnet #csharp #инженерныештучки #heavywednesday
В прошлый раз мы увидели, как JIT переводит IL в машинный код процессора🤩
Там я упомянул, что обычно метод сначала компилируется «наспех», чтобы он вообще работал. А если метод окажется горячим🌚 , то позже он компилируется заново, уже с оптимизациями📈
Разберёмся, почему так, что такое горячий метод, и что за оптимизации👇
🎯 Дилемма: быстрый старт или быстрый код
У JIT конфликт интересов: по-хорошему, надо бы оптимизировать весь код, чтобы он был очень быстрым и эффективным. Но оптимизация — дело небыстрое. Если возиться с каждым методом при первом же вызове, программа будет мучительно долго стартовать 🐢
При этом некоторые методы вызываются лишь несколько раз за время выполнения программы, так что тратить время на их оптимизацию бессмысленно. А те, что вызываются постоянно, наоборот, хочется сделать супер-быстрыми 🔥
2️⃣ Решение — два уровня компиляции
🟢 Tier0 — быстрый перевод без оптимизаций, чтобы метод поскорее заработал
🟢 Tier1 — компиляция с полным набором оптимизаций. Применяется только к тем методам, которые вызываются достаточно часто
🧰 Какие именно оптимизации?
🟢 Встраивание: тело короткого метода подставляется прямо в место вызова. Экономим на переходе к нему и открываем дорогу другим оптимизациям
🟢 Раскладка по регистрам: нужные значения переменных, аргументов, и промежуточных вычислений держатся в регистрах процессора — самой быстрой памяти, а не гоняются в оперативную память
🟢 Разворачивание циклов: короткий цикл превращается в несколько дублирований тела цикла, чтобы реже проверять условие выхода
🟢 Выкидывание лишнего: вычисления, результат которых никто не читает, просто убираются
♨️ Как JIT понимает, что метод горячий
У каждого метода есть счётчик вызовов. Пока его значение небольшое, вызывается та версия метода, которая была скомпилирована JIT при первом вызове, то есть на уровне Tier0. Как только счётчик переваливает за определённый порог, к методу в фоне применяется Tier1, и следующие вызовы идут уже к оптимизированному методу⚡️
По умолчанию этот порог составляет 30 вызовов (
DOTNET_TC_CallCountThreshold). Это можно увидеть вжиую, если включить сводку JIT (DOTNET_JitDisasmSummary=1) и гонять метод нужное число раз:calls=30 → JIT compiled Compute [Tier0]
calls=31 → JIT compiled Compute [Tier0]
JIT compiled Compute [Tier1]
До 30 вызовов включительно метод так и остаётся скомпилированным на Tier0. На 31-м же видно, что метод скомпилировался заново, уже на Tier1 ✨
🅰️ Что запомнить
🟢 Первые вызовы горячего метода идут по версии, скомпилированной на Tier0, то есть без особых оптимизаций
🟢 Если метод вызывается достаточно часто (по умолчанию, больше 30 раз), то он компилируется на Tier1, с оптимизациями
🤯 Вот это поворот
Заглавный дикпик заспойлерил, что на самом деле уровней больше😱
Ну или не прям уровней, а разновидностей Tier0 и Tier1, но это уже разберём в следующий раз 👉
🧑💻dp🥁
#dotnet #csharp #инженерныештучки #heavywednesday

















