Своевременная компиляция - Just-in-time compilation

В вычисление, вовремя (JIT) сборник (также динамический перевод или же компиляции во время выполнения)[1] это способ выполнения компьютерный код это включает сборник во время выполнения программы - при время выполнения - а не до казни.[2] Чаще всего это состоит из исходный код или чаще байт-код перевод на Машинный код, который затем выполняется напрямую. Система, реализующая JIT-компилятор, обычно непрерывно анализирует выполняемый код и определяет части кода, в которых ускорение, полученное от компиляции или перекомпиляции, перевесит накладные расходы на компиляцию этого кода.

JIT-компиляция - это комбинация двух традиционных подходов к трансляции в машинный код: опережающая компиляция (AOT) и интерпретация - и сочетает в себе некоторые преимущества и недостатки обоих.[2] Грубо говоря, JIT-компиляция сочетает в себе скорость скомпилированного кода с гибкостью интерпретации с накладными расходами интерпретатора и дополнительными накладными расходами на компиляцию (а не только на интерпретацию). JIT-компиляция - это форма динамическая компиляция, и позволяет адаптивная оптимизация Такие как динамическая перекомпиляция и микроархитектура -конкретные ускорения[nb 1][3] Интерпретация и JIT-компиляция особенно подходят для языки динамического программирования, поскольку исполняющая система может обрабатывать с опозданием типы данных и обеспечить гарантии безопасности.

Приложения

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

Распространенной реализацией JIT-компиляции является сначала компиляция AOT в байт-код (виртуальная машина код), известный как компиляция байт-кода, а затем выполнить JIT-компиляцию в машинный код (динамическую компиляцию), а не интерпретацию байт-кода. Это улучшает производительность во время выполнения по сравнению с интерпретацией за счет задержки из-за компиляции. Компиляторы JIT переводят непрерывно, как и интерпретаторы, но кэширование скомпилированного кода минимизирует задержку при выполнении того же кода в будущем во время данного прогона. Поскольку компилируется только часть программы, задержка значительно меньше, чем если бы вся программа была скомпилирована до выполнения.

Обзор

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

Напротив, традиционный интерпретируемая виртуальная машина будет просто интерпретировать байт-код, как правило, с гораздо меньшей производительностью. Немного устный переводчикs даже интерпретировать исходный код, без предварительной компиляции в байт-код, с еще худшей производительностью. Статически скомпилированный код или же собственный код компилируется перед развертыванием. А динамическая среда компиляции это тот, в котором компилятор может использоваться во время выполнения. Общая цель использования методов JIT - достичь или превзойти производительность статической компиляции при сохранении преимуществ интерпретации байт-кода: большая часть «тяжелой работы» по синтаксическому анализу исходного исходного кода и выполнению базовой оптимизации часто выполняется во время компиляции, до развертывания: компиляция из байт-кода в машинный код выполняется намного быстрее, чем компиляция из исходного кода. Развернутый байт-код переносим, ​​в отличие от нативного кода. Поскольку среда выполнения контролирует компиляцию, как и интерпретируемый байт-код, она может выполняться в безопасной песочнице. Компиляторы из байт-кода в машинный код писать легче, потому что переносимый компилятор байт-кода уже проделал большую часть работы.

Код JIT обычно обеспечивает гораздо лучшую производительность, чем интерпретаторы. Кроме того, в некоторых случаях он может предложить лучшую производительность, чем статическая компиляция, поскольку многие оптимизации возможны только во время выполнения:[4][5]

  1. Компиляцию можно оптимизировать для целевого процессора и модели операционной системы, в которой выполняется приложение. Например, JIT может выбрать SSE2 векторные инструкции ЦП, когда он обнаруживает, что ЦП их поддерживает. Чтобы получить такой уровень специфичности оптимизации с помощью статического компилятора, нужно либо скомпилировать двоичный файл для каждой предполагаемой платформы / архитектуры, либо включить несколько версий частей кода в один двоичный файл.
  2. Система может собирать статистику о том, как программа фактически работает в среде, в которой она находится, а также может перекомпилировать и перекомпилировать для достижения оптимальной производительности. Однако некоторые статические компиляторы также могут принимать в качестве входных данных информацию профиля.
  3. Система может выполнять глобальную оптимизацию кода (например, встраивание библиотечных функций) без потери преимуществ динамической компоновки и без накладных расходов, присущих статическим компиляторам и компоновщикам. В частности, при выполнении глобальных встроенных подстановок процессу статической компиляции могут потребоваться проверки во время выполнения и гарантировать, что виртуальный вызов произойдет, если фактический класс объекта переопределяет встроенный метод, и может потребоваться обработка проверок граничных условий при доступе к массиву внутри петель. При своевременной компиляции во многих случаях эту обработку можно вывести из цикла, что часто приводит к значительному увеличению скорости.
  4. Хотя это возможно со статически скомпилированными языками со сборкой мусора, система байт-кода может более легко переупорядочить исполняемый код для лучшего использования кеша.

Поскольку JIT должен отображать и выполнять собственный двоичный образ во время выполнения, для истинных JIT с машинным кодом требуются платформы, которые позволяют выполнять данные во время выполнения, что позволяет использовать такие JIT на Гарвардская архитектура машина на основе невозможна - то же самое можно сказать и о некоторых операционных системах и виртуальных машинах. Однако специальный тип "JIT" потенциально может нет нацелена на архитектуру ЦП физической машины, а скорее на оптимизированный байт-код виртуальной машины, где преобладают ограничения на исходный машинный код, особенно когда виртуальная машина этого байт-кода в конечном итоге использует JIT для собственного кода.[6]

Задержка запуска и оптимизация

JIT вызывает небольшую или заметную задержку в начальном выполнении приложения из-за времени, затрачиваемого на загрузку и компиляцию байт-кода. Иногда эту задержку называют «время запуска» или «время прогрева». В целом, чем больше выполняется JIT-оптимизация, тем лучше будет сгенерированный код, но начальная задержка также увеличится. Поэтому JIT-компилятор должен найти компромисс между временем компиляции и качеством кода, который он надеется сгенерировать. Время запуска может включать в себя увеличение операций, связанных с вводом-выводом, в дополнение к JIT-компиляции: например, rt.jar файл данных класса для Виртуальная машина Java (JVM) составляет 40 МБ, и JVM должна искать много данных в этом контекстно огромном файле.[7]

Одна возможная оптимизация, используемая Sun's HotSpot Виртуальная машина Java, объединяет интерпретацию и JIT-компиляцию. Код приложения изначально интерпретируется, но JVM отслеживает, какие последовательности байт-код часто выполняются и переводят их в машинный код для прямого выполнения на оборудовании. Для байт-кода, который выполняется всего несколько раз, это экономит время компиляции и снижает начальную задержку; для часто выполняемого байт-кода JIT-компиляция используется для работы на высокой скорости после начальной фазы медленной интерпретации. Кроме того, поскольку программа тратит большую часть времени на выполнение меньшей части своего кода, время компиляции значительно сокращается. Наконец, во время первоначальной интерпретации кода статистика выполнения может быть собрана перед компиляцией, что помогает выполнить лучшую оптимизацию.[8]

Правильный компромисс может варьироваться в зависимости от обстоятельств. Например, виртуальная машина Java Sun имеет два основных режима - клиентский и серверный. В клиентском режиме выполняется минимальная компиляция и оптимизация, чтобы сократить время запуска. В режиме сервера выполняется обширная компиляция и оптимизация, чтобы максимизировать производительность после запуска приложения, жертвуя временем запуска. Другие JIT-компиляторы Java использовали измерение времени выполнения, определяющее количество выполнений метода, в сочетании с размером байт-кода метода в качестве эвристики, чтобы решить, когда компилировать.[9] Еще один использует количество выполнений в сочетании с обнаружением циклов.[10] В общем, гораздо сложнее точно предсказать, какие методы следует оптимизировать в краткосрочных приложениях, чем в долго работающих.[11]

Генератор собственных изображений (Ngen) автор: Microsoft это еще один подход к уменьшению начальной задержки.[12] Ngen предварительно компилирует (или "pre-JIT") байт-код в Общий промежуточный язык изображение в машинный код. В результате компиляция среды выполнения не требуется. .NET Framework 2.0 поставляется с Visual Studio 2005 запускает Ngen для всех библиотек DLL Microsoft сразу после установки. Предварительная обработка позволяет сократить время запуска. Однако качество генерируемого кода может быть не таким хорошим, как у JIT-кода, по тем же причинам, по которым код компилируется статически, без профильная оптимизация, не может быть так же хорош, как JIT-скомпилированный код в крайнем случае: отсутствие данных профилирования для управления, например, встроенным кэшированием.[13]

Также существуют реализации Java, которые объединяют Компилятор AOT (опережающий время) либо с JIT-компилятором (Excelsior JET ) или переводчик (Компилятор GNU для Java ).

История

Самый ранний опубликованный JIT-компилятор обычно приписывают работе над LISP к Джон Маккарти в 1960 г.[14] В своей основополагающей статье Рекурсивные функции символьных выражений и их машинное вычисление, Часть I, он упоминает функции, которые транслируются во время выполнения, тем самым избавляя от необходимости сохранять вывод компилятора в перфокарты[15] (хотя это было бы более точно известно как "Скомпилируйте и начните систему "). Еще один ранний пример - Кен Томпсон, который в 1968 году подал одно из первых приложений обычные выражения, здесь для сопоставление с образцом в текстовом редакторе QED.[16] Для скорости Томпсон реализовал сопоставление регулярных выражений с помощью JIT для IBM 7094 код на Совместимая система разделения времени.[14] Влиятельный метод получения скомпилированного кода из интерпретации был впервые предложен Джеймс Дж. Митчелл в 1970 году, который он реализовал для экспериментального языка LC².[17][18]

Болтовня (c. 1983) впервые открыл новые аспекты JIT-компиляций. Например, перевод в машинный код выполнялся по запросу, а результат кэшировался для дальнейшего использования. Когда памяти становилось мало, система удаляла часть этого кода и регенерировала его, когда он снова понадобился.[2][19] Солнце Себя язык значительно улучшил эти методы и в какой-то момент был самой быстрой системой Smalltalk в мире; достигая до половины скорости оптимизированного C[20] но с полностью объектно-ориентированным языком.

Sun отказалась от Self, но исследования перешли на язык Java. Термин «Своевременная компиляция» был заимствован из производственного термина «Вовремя "и популяризировал Java, а Джеймс Гослинг использовал термин с 1993 года.[21] В настоящее время JITing используется в большинстве реализаций Виртуальная машина Java, так как HotSpot опирается на эту исследовательскую базу и широко использует ее.

Проект HP Dynamo[22] был экспериментальным JIT-компилятором, в котором формат «байт-кода» и формат машинного кода были одинаковыми; система превратила машинный код PA-6000 в PA-8000 Машинный код. Как ни странно, это привело к ускорению, в некоторых случаях на 30%, поскольку выполнение этого разрешало оптимизацию на уровне машинного кода, например, встраивание кода для лучшего использования кэша и оптимизацию вызовов динамических библиотек и многих других оптимизаций времени выполнения, которые обычно компиляторы не могут попытаться.[23][24]

В ноябре 2020 г. PHP 8.0 представил JIT-компилятор.[25]


Безопасность

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

Реализация JIT-компиляции состоит из компиляции исходного кода или байтового кода в машинный код и его выполнения. Обычно это выполняется непосредственно в памяти - JIT-компилятор выводит машинный код непосредственно в память и немедленно выполняет его, а не выводит на диск и затем вызывает код как отдельную программу, как при обычной предварительной компиляции. В современных архитектурах это сталкивается с проблемой из-за исполняемая защита пространства - произвольная память не может быть выполнена, иначе есть потенциальная дыра в безопасности. Таким образом, память должна быть помечена как исполняемая; по соображениям безопасности это должно быть сделано после код был записан в память и помечен как доступный только для чтения, так как доступная для записи / исполняемая память является дырой в безопасности (см. W ^ X ).[26] Например, JIT-компилятор Firefox для Javascript представил эту защиту в выпускной версии Firefox 46.[27]

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

Смотрите также

Примечания

  1. ^ Компиляторы Ahead-of-Time также могут ориентироваться на определенные микроархитектуры, но разница между AOT и JIT в этом вопросе заключается в переносимости. JIT может визуализировать код, адаптированный к текущему процессору во время выполнения, тогда как AOT вместо оптимизации для обобщенного подмножества uarches должен заранее знать целевой ЦП - такой код может не только не работать на других типах ЦП, но и может быть совершенно нестабильным.

Рекомендации

Цитаты

  1. ^ Языки, компиляторы и системы времени выполнения, Мичиганский университет, компьютерные науки и инженерия, получено 15 марта, 2018
  2. ^ а б c Эйкок 2003.
  3. ^ "Использует ли JIT преимущества моего процессора?". WebLog Дэвида Нотарио. Получено 2018-12-03.
  4. ^ Кроче, Луи. «Как раз вовремя, подборка» (PDF). Колумбийский университет. Архивировано из оригинал (PDF) на 2018-05-03.
  5. ^ «Каковы преимущества JIT перед компиляцией AOT». Переполнение стека. 21 января 2010 г.
  6. ^ "javascript - Скомпилировать язык на основе JIT в Webassembly". Переполнение стека. Получено 2018-12-04.
  7. ^ Хаасе, Чет (май 2007 г.). "Consumer JRE: более экономичная, более серьезная технология Java". Sun Microsystems. Получено 2007-07-27. На уровне ОС все эти мегабайты должны быть прочитаны с диска, что является очень медленной операцией. На самом деле убийца - время поиска диска; последовательное чтение больших файлов происходит относительно быстро, но поиск нужных нам битов - нет. Таким образом, даже несмотря на то, что нам нужна только небольшая часть данных в этих больших файлах для любого конкретного приложения, тот факт, что мы ищем повсюду в файлах, означает, что на диске много активности.
  8. ^ «Архитектура механизма производительности Java HotSpot». Oracle.com. Получено 2013-07-05.
  9. ^ Шиллинг, Джонатан Л. (февраль 2003 г.). «Простейшая эвристика может быть лучшей в компиляторах Java JIT» (PDF). Уведомления SIGPLAN. 38 (2): 36–46. Дои:10.1145/772970.772975. Архивировано из оригинал (PDF) на 2015-09-24.
  10. ^ Тошио Суганума, Тошиаки Ясуе, Мотохиро Кавахито, Хидеаки Комацу, Тошио Накатани, «Среда динамической оптимизации для JIT-компилятора Java», Материалы 16-й конференции ACM SIGPLAN по объектно-ориентированному программированию, системам, языкам и приложениям (OOPSLA '01), стр. 180–195, 14–18 октября 2001 г.
  11. ^ Мэтью Арнольд, Майкл Хинд, Барбара Г. Райдер, «Эмпирическое исследование выборочной оптимизации», Труды 13-го Международного семинара по языкам и компиляторам для параллельных вычислений - исправленные статьиС. 49–67, 10–12 августа 2000 г.
  12. ^ «Генератор собственных изображений (Ngen.exe)». Msdn2.microsoft.com. Получено 2013-07-05.
  13. ^ Мэтью Р. Арнольд, Стивен Финк, Дэвид П. Гроув, Майкл Хинд и Питер Ф. Суини "Обзор адаптивной оптимизации виртуальных машин ", Труды IEEE, 92 (2), февраль 2005 г., стр. 449–466.
  14. ^ а б Эйкок 2003, 2. Методы JIT-компиляции, 2.1 Genesis, стр. 98.
  15. ^ Маккарти, Дж. (Апрель 1960 г.). «Рекурсивные функции символьных выражений и их машинное вычисление, Часть I». Коммуникации ACM. 3 (4): 184–195. CiteSeerX  10.1.1.111.8833. Дои:10.1145/367177.367199.
  16. ^ Томпсон 1968.
  17. ^ Эйкок 2003, 2. Методы JIT-компиляции, 2.2 LC², с. 98–99.
  18. ^ Митчелл, Дж. (1970). «Проектирование и построение гибких и эффективных систем интерактивного программирования». Цитировать журнал требует | журнал = (помощь)
  19. ^ Deutsch, L.P .; Шиффман, А. (1984). «Эффективное внедрение системы Smalltalk-80» (PDF). POPL '84: Материалы 11-го симпозиума ACM SIGACT-SIGPLAN по принципам языков программирования: 297–302. Дои:10.1145/800017.800542. ISBN  0-89791-125-3. Архивировано из оригинал (PDF) 18 июня 2004 г.
  20. ^ [1] В архиве 24 ноября 2006 г. Wayback Machine
  21. ^ Эйкок 2003, 2.14 Java, стр. 107, сноска 13.
  22. ^ «Динамо: прозрачная система динамической оптимизации» Васант Бала, Эвелин Дюстервальд, Санджив Банерджиа - PLDI '00 Труды конференции ACM SIGPLAN 2000 по разработке и реализации языков программирования - страницы с 1 по 12 - Дои:10.1145/349299.349303 Проверено 28 марта 2012 г.
  23. ^ Джон Джаннотти. "Динамо HP - Страница 1 - (3/2000)". Ars Technica. Получено 2013-07-05.
  24. ^ «Проект HP Dynamo». Архивировано 19 октября 2002 года.. Получено 2016-04-12.CS1 maint: неподходящий URL (ссылка на сайт)
  25. ^ Тунг, Лиам (27 ноября 2020 г.). «Язык программирования PHP 8 отсутствует: этот новый JIT-компилятор указывает на лучшую производительность». ZDNet. Получено 28 ноября 2020.
  26. ^ "Как JIT - введение ", Эли Бендерский, 5 ноября 2013 г., 5:59
  27. ^ Де Моой, янв. "W ^ X JIT-код включен в Firefox". Ян Де Муй. Получено 11 мая 2016.

Источники

внешняя ссылка