Компіляція C # JIT та .NET


86

Я трохи заплутався в деталях того, як працює компілятор JIT. Я знаю, що C # компілюється до IL. Перший раз, коли він запускається, це JIT'd. Чи передбачає це переклад у рідний код? Чи взаємодіє середовище виконання .NET (як віртуальна машина?) Із кодом JIT'd? Я знаю, що це наївно, але я справді заплутав себе. У мене завжди було враження, що збірки не інтерпретуються середовищем виконання .NET, але я не розумію деталей взаємодії.

Відповіді:


83

Так, код JIT'ing IL передбачає переведення IL у власні машинні інструкції.

Так, середовище виконання .NET взаємодіє з власним машинним кодом JIT, в тому сенсі, що середовище виконання володіє блоками пам'яті, зайнятими власним машинним кодом, викликами середовища виконання у власний машинний код тощо.

Ви праві, що середовище виконання .NET не інтерпретує код IL у ваших збірках.

Що трапляється, коли виконання досягає функції або блоку коду (наприклад, речення else блоку if), який ще не був скомпільований JIT у власний машинний код, JIT'r викликається для компіляції цього блоку IL у власний машинний код . Після цього виконання програми вводить свіжовипущений машинний код для виконання логіки програми. Якщо під час виконання цього власного машинного коду виконання досягає виклику функції до функції, яка ще не була скомпільована до машинного коду, JIT'r викликається для компіляції цієї функції "вчасно". І так далі.

JIT'r не обов'язково компілює всю логіку тіла функції в машинний код одночасно. Якщо функція має оператори if, блоки операторів пропозицій if або else не можуть бути скомпільовані JIT, поки виконання фактично не пройде через цей блок. Шляхи коду, які не виконувались, залишаються у формі IL, доки їх не виконають.

Складений власний машинний код зберігається в пам'яті, щоб його можна було використовувати знову при наступному запуску цього розділу коду. Другий раз, коли ви викликаєте функцію, вона запуститься швидше, ніж у перший раз, оскільки вдруге не потрібен крок JIT.

У настільному .NET власний машинний код зберігається в пам'яті протягом усього терміну дії додаткового домену. У .NET CF власний машинний код може бути викинутий, якщо в програмі мало пам'яті. Це буде JIT, компільований знову з вихідного коду IL, наступного разу, коли виконання пройде через цей код.


14
Невелика педантична корекція - 'JIT'r не обов'язково компілює всю логіку тіла функції' - У теоретичній реалізації, що може бути так, але в існуючих реалізаціях середовища виконання .NET, компілятор JIT компілює ціле методи відразу.
Paul Alexander

18
Щодо того, чому це робиться, це насамперед тому, що не-jit-компілятор не може припустити, що певні оптимізації доступні на будь-якій цільовій платформі. Єдиними варіантами є або компіляція до найменшого загального знаменника, або, найчастіше, компіляція декількох версій, кожна з яких націлена на власну платформу. JIT усуває цей недолік, оскільки остаточний переклад в машинний код виконується на цільовій машині, де компілятор знає, які доступні оптимізації.
Chris Shain,

1
Кріс, навіть думав, що JIT знає, які оптимізації доступні, він не встиг застосувати якісь значні оптимізації. Можливо, NGEN є потужнішим.
Григорій

2
@Grigory Так, NGEN має розкіш часу, якого не робить компілятор JIT, але існує безліч рішень кодегенів, які JIT може прийняти для покращення продуктивності скомпільованого коду, на розбір яких не потрібно багато часу. Наприклад, компілятор JIT може вибрати набори команд, щоб найкраще відповідати доступному обладнанню, знайденому під час виконання, не додаючи значного часу на крок компіляції JIT. Я не думаю, що .NET JITter робить це суттєво, але це можливо.
dthorpe

24

Код "компілюється" на проміжну мову Microsoft, яка схожа на формат збірки.

Коли ви двічі клацаєте на виконуваному файлі, Windows завантажується, mscoree.dllа потім налаштовує середовище CLR і запускає код вашої програми. Компілятор JIT починає читати код MSIL у вашій програмі та динамічно компілює код в інструкції x86, які може виконувати центральний процесор.


1

.NET використовує проміжну мову, що називається MSIL, іноді скорочується як IL. Компілятор читає ваш вихідний код і видає MSIL. Під час запуску програми компілятор .NET Just In Time (JIT) зчитує ваш код MSIL і створює виконувану програму в пам'яті. Ви не побачите, щоб щось із цього сталося, але корисно знати, що відбувається за лаштунками.


1

Я опишу компіляцію коду ІЛ у вбудовані інструкції процесора на прикладі нижче.

public class Example 
{
    static void Main() 
    {
        Console.WriteLine("Hey IL!!!");
    }
}

В першу чергу CLR знає всі подробиці про тип і про те, який метод, що викликається з цього типу, зумовлений метаданими.

Коли CLR починає виконувати IL у власній інструкції центрального процесора, тоді CLR виділяє внутрішні структури даних для кожного типу, на який посилається код Main.

У нашому випадку у нас є лише один тип консолі, тому CLR виділить одну внутрішню структуру даних через цю внутрішню структуру, ми будемо керувати доступом до вказаних типів

всередині цієї структури даних CLR містить записи про всі методи, визначені цим типом. Кожен запис містить адресу, де можна знайти реалізацію методу.

При ініціалізації цієї структури CLR встановлює кожен запис у недокументованій FUNCTION, що міститься всередині самого CLR. І, як ви можете здогадатися, ця FUNCTION - це те, що ми називаємо компілятором JIT.

Загалом, ви можете розглядати JIT-компілятор як функцію CLR, яка вводить ІЛ у власні інструкції процесора. Дозвольте мені докладно показати вам, як буде цей процес на нашому прикладі.

1. Коли Мейн робить свій перший дзвінок до WriteLine, викликається функція JITCompiler.

2. Функція компілятора JIT знає, який метод викликається, і який тип визначає цей метод.

Потім Jit Compiler здійснює пошук збірки, де визначено цей тип, і отримує код IL для методу, визначеного цим типом, у нашому випадку код IL методу WriteLine.

4. Компілятор JIT виділяє блок пам'яті DYNAMIC , після чого JIT перевіряє та компілює код IL у власний код процесора та зберігає цей код процесора в цьому блоці пам'яті.

5. Потім компілятор JIT повертається до внутрішнього запису структури даних і замінює адресу (яка в першу чергу посилається на реалізацію коду IL-запису WriteLine) новим адресно-динамічно створеним блоком пам'яті, який містить вбудовані вказівки процесора WriteLine.

6. Зрештою, функція компілятора JIT переходить до коду в блоці пам'яті. Цей код є реалізацією методу WriteLine.

7. Після реалізації WriteLine код повертається до головного коду, який продовжує виконуватись як зазвичай.


0

.NET Framework використовує середовище CLR для створення MSIL (проміжної мови Microsoft), яка також називається IL. Компілятор читає ваш вихідний код, і коли ви збираєте / компілюєте свій проект, він генерує MSIL. Тепер, коли ви нарешті запустите свій проект, починає діяти .NET JIT ( щойно введений компілятор ). JIT читає ваш код MSIL і створює власний код (це інструкції x86), який може бути легко виконаний процесором. JIT читає всі інструкції MSIL і виконує їх рядково за рядком.

Якщо вам цікаво подивитися, що відбувається за лаштунками, на це вже відповіли. Будь ласка, дотримуйтесь - тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.