Подтвердить что ты не робот

Почему .net использует компилятор JIT вместо того, чтобы просто компилировать код один раз на целевой машине?

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

4b9b3361

Ответ 1

Есть две вещи, которые можно получить, используя промежуточный формат, например .NET или Java:

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

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

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

Если вы начинаете кэшировать код JIT в сеансах, вам нужно отслеживать, что уже было скомпилировано, а затем сохранить его на диск. Для большой программы у вас может быть много встроенного кода для загрузки с диска. Диск ввода/вывода довольно дорогой, поэтому может потребоваться больше времени, чтобы ждать диск, чем повторно использовать JIT. Кроме того, вам необходимо отслеживать, как долго этот кеш можно использовать. Если аппаратное обеспечение изменится, вы можете захотеть повторно использовать JIT, чтобы применить некоторые новые оптимизации. Если программа изменится, вы не сможете использовать какой-либо старый скомпилированный код. Если компилятор времени выполнения изменится, ошибка безопасности может быть исправлена, и вам необходимо перекомпилировать, чтобы убедиться, что ошибка не сохраняется в вашем собственном коде.

В принципе, компилятору JIT неожиданно предстоит сделать намного больше работы (включая дисковый ввод-вывод для работы с кешем) и становится намного сложнее и медленнее, уменьшая точку JIT'ing.

Теперь это не означает, что иногда бывает полезно предварительно скомпилировать определенные сборки, и, как указывает Мэтью Феррейра, это то, что может сделать инструмент ngen, но в общем случае это просто не стоит делая это, потому что компиляция JIT часто бывает более чем достаточно быстрой.

Ответ 2

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

Ответ 3

Я не пишу компиляторы или run-times, поэтому я не могу сказать это наверняка, но я не верю just-in-time = каждый раз. Это просто означает, что мы подождем, пока нам действительно не понадобится компиляция, но как только компиляция будет выполнена для этого экземпляра, она не будет скомпилирована снова.

Ответ 4

Еще одно преимущество компиляции JIT: код, написанный 10 или 15 лет назад, может значительно повысить производительность благодаря простому запуску с использованием современного JIT без каких-либо изменений кода.

Ответ 5

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

.NET кэширует результаты компиляции CIL в собственный код на заданную цель, но это не тривиально:

  • Существует компромисс между загрузкой кэшированных данных с диска и перекомпиляцией "на лету".
  • Любая форма генерации кода во время выполнения требует возможности компиляции "на лету". Это не только использование System.Reflection.Emit, но и переопределение типов из динамически загружаемых сборок и даже создание новых типов значений во время полиморфной рекурсии.

Ответ 6

Если вы скомпилируете непосредственно машинный код, то вы нацелились на определенную систему:

Code --> Machine Code

Однако в .NET, Java и т.д. то, что вы нацеливаете, - это виртуальная машина. Это создает код, который может интерпретировать любая совместимая с .NET совместимая виртуальная машина, и, в свою очередь, JIT в настоящий машинный код.

Итак, в .NET:

Code --> IL --> VM (executing) --> JIT'd Machine Code

Изначально это выглядит более сложным, но рассмотрите возможность ориентации на несколько архитектур:

Code(x86) --> Machine Code(x86)
Code(ppc) --> Machine Code(ppc)
etc... (1x code-set per target)

Против

Code --> IL --> VM(x86) (executing) --> JIT'd x86 Machine Code
                VM(ppc) (executing) --> JIT'd ppc Machine Code
                etc...

Моя иллюстрация грубая, но вы заметите, что один пакет .NET-кода может быть нацелен и запущен на нескольких платформах.