stringtranslate.com

Компиляция точно в срок

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

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

История

Самый ранний опубликованный JIT-компилятор обычно приписывают работе Джона Маккарти над LISP в 1960 году . необходимость сохранять выходные данные компилятора на перфокартах [5] (хотя точнее это было бы называть « системой компиляции и запуска »). Другой ранний пример принадлежит Кену Томпсону , который в 1968 году предложил одно из первых применений регулярных выражений , в данном случае для сопоставления с образцом в текстовом редакторе QED . [6] Для ускорения Томпсон реализовал сопоставление регулярных выражений путем JIT-компиляции с кодом IBM 7094 в совместимой системе разделения времени . [4] Влиятельная техника получения скомпилированного кода из интерпретации была впервые предложена Джеймсом Г. Митчеллом в 1970 году и реализована для экспериментального языка LC² . [7] [8]

Smalltalk (около 1983 г.) стал пионером в новых аспектах JIT-компиляции. Например, трансляция в машинный код выполнялась по требованию, а результат кэшировался для последующего использования. Когда памяти становилось недостаточно, система удаляла часть этого кода и восстанавливала его, когда он снова требовался. [2] [9] Язык Sun Self значительно усовершенствовал эти методы и в какой-то момент был самой быстрой системой Smalltalk в мире, достигая половины скорости оптимизированного C [10] , но с полностью объектно-ориентированным языком.

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

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

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

Дизайн

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

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

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

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

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

Производительность

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

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

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

Native Image Generator (Ngen) от Microsoft — это еще один подход к уменьшению начальной задержки. [24] Ngen предварительно компилирует (или «предварительно-JIT») байт-код из образа Common Intermediate Language в собственный машинный код. В результате компиляция во время выполнения не требуется. .NET Framework 2.0, поставляемый с Visual Studio 2005, запускает Ngen во всех библиотеках Microsoft DLL сразу после установки. Предварительная синхронизация позволяет сократить время запуска. Однако качество генерируемого им кода может быть не таким хорошим, как код, скомпилированный JIT, по тем же причинам, по которым код, скомпилированный статически, без оптимизации на основе профиля , не может быть таким же хорошим, как код, скомпилированный JIT, в крайнем случае: отсутствие профилирования данных для управления, например, встроенным кэшированием. [25]

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

JIT-компиляция может не достичь своей цели, а именно перехода в устойчивое состояние повышенной производительности после короткого начального периода прогрева. [26] [27] На восьми разных виртуальных машинах Барретт и др. (2017) измерили шесть широко используемых микротестов, которые обычно используются разработчиками виртуальных машин в качестве целей оптимизации, многократно запуская их в рамках одного выполнения процесса. [28] В Linux они обнаружили, что от 8,7% до 9,6% выполнений процессов не смогли достичь устойчивого состояния производительности, от 16,7% до 17,9% вошли в устойчивое состояние снижения производительности после периода прогрева и 56,5% пар определенных виртуальная машина, выполняющая определенный тест, не смогла последовательно увидеть устойчивое отсутствие снижения производительности при нескольких выполнениях (т. е. по крайней мере одно выполнение не смогло достичь устойчивого состояния или наблюдалось снижение производительности в устойчивом состоянии). Даже там, где достигалось улучшенное устойчивое состояние, иногда требовались многие сотни итераций. [29] Трайни и др. (2022) вместо этого сосредоточились на виртуальной машине HotSpot, но с гораздо более широким набором тестов, [30] обнаружив, что 10,9% выполнений процессов не смогли достичь устойчивого состояния производительности, а 43,5% тестов не достигли стабильного состояния. через несколько исполнений. [31]

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

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

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

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

Использование

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

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

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

Примечания

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

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

  1. ^ Языки, компиляторы и системы выполнения, Мичиганский университет, информатика и инженерия , получено 15 марта 2018 г.
  2. ^ abc Эйкок 2003.
  3. ^ «Использует ли JIT преимущества моего процессора?». Веб-журнал Дэвида Нотарио . Проверено 03 декабря 2018 г.
  4. ^ ab Aycock 2003, 2. Методы JIT-компиляции, 2.1 Genesis, p. 98.
  5. ^ Маккарти, Дж. (апрель 1960 г.). «Рекурсивные функции символьных выражений и их машинное вычисление, Часть I». Коммуникации АКМ . 3 (4): 184–195. CiteSeerX 10.1.1.111.8833 . дои : 10.1145/367177.367199. S2CID  1489409. 
  6. ^ Томпсон 1968.
  7. ^ Эйкок 2003, 2. Методы JIT-компиляции, 2.2 LC², стр. 98–99.
  8. ^ Митчелл, JG (1970). «Проектирование и построение гибких и эффективных систем интерактивного программирования». {{cite journal}}: Требуется цитировать журнал |journal=( помощь )
  9. ^ Дойч, LP; Шиффман, AM (1984). «Эффективное внедрение системы smalltalk-80» (PDF) . Материалы 11-го симпозиума ACM SIGACT-SIGPLAN по принципам языков программирования — POPL '84 . стр. 297–302. дои : 10.1145/800017.800542. ISBN 0-89791-125-3. S2CID  3045432. Архивировано из оригинала (PDF) 18 июня 2004 г.
  10. ^ "97-pep.ps" . www.research.sun.com . Архивировано из оригинала 24 ноября 2006 года . Проверено 15 января 2022 г.
  11. ^ Эйкок 2003, 2.14 Java, с. 107, сноска 13.
  12. ^ «Динамо: прозрачная система динамической оптимизации» . Васант Бала, Эвелин Дюстервальд, Санджив Банерджиа. PLDI '00 Материалы конференции ACM SIGPLAN 2000 по проектированию и реализации языков программирования. страницы с 1 по 12. DOI 10.1145/349299.349303. Проверено 28 марта 2012 г.
  13. ^ Джон Джаннотти. «Динамо HP». Арс Техника . Проверено 5 июля 2013 г.
  14. ^ "Проект HP Dynamo" . Архивировано из оригинала 19 октября 2002 года . Проверено 12 апреля 2016 г.{{cite web}}: CS1 maint: unfit URL (link)
  15. Тунг, Лиам (27 ноября 2020 г.). «Вышел язык программирования PHP 8: этот новый JIT-компилятор обеспечивает лучшую производительность». ЗДНет . Проверено 28 ноября 2020 г.
  16. ^ Кроче, Луи. «Подборка точно в срок» (PDF) . Колумбийский университет . Архивировано из оригинала (PDF) 3 мая 2018 г.
  17. ^ «Каковы преимущества JIT-компиляции по сравнению с AOT-компиляцией» . Переполнение стека . 21 января 2010 г.
  18. ^ «Компилируйте язык на основе JIT в Webassembly» . Переполнение стека . Проверено 4 декабря 2018 г.
  19. ^ Хаазе, Чет (май 2007 г.). «Потребительская JRE: более экономичная, более эффективная технология Java». Сан Микросистемс . Проверено 27 июля 2007 г.
  20. ^ «Архитектура механизма производительности Java HotSpot» . Oracle.com . Проверено 5 июля 2013 г.
  21. ^ Шиллинг, Джонатан Л. (февраль 2003 г.). «Самая простая эвристика может быть лучшей в JIT-компиляторах Java» (PDF) . Уведомления SIGPLAN . 38 (2): 36–46. дои : 10.1145/772970.772975. S2CID  15117148. Архивировано из оригинала (PDF) 24 сентября 2015 г.
  22. ^ Тосио Суганума, Тошиаки Ясуэ, Мотохиро Кавахито, Хидеаки Комацу, Тошио Накатани, «Среда динамической оптимизации для JIT-компилятора», Материалы 16-й конференции ACM SIGPLAN по объектно-ориентированному программированию, системам, языкам и приложения (OOPSLA '01), стр. 180–195, 14–18 октября 2001 г.
  23. ^ Мэтью Арнольд, Майкл Хинд, Барбара Г. Райдер, «Эмпирическое исследование выборочной оптимизации», Труды 13-го международного семинара по языкам и компиляторам для параллельных вычислений, пересмотренные статьи , стр. 49–67, 10–12 августа 2000 г. .
  24. ^ «Собственный генератор изображений (Ngen.exe)» . Msdn2.microsoft.com. 5 декабря 2006 года . Проверено 5 июля 2013 г.
  25. ^ Суини, Арнольд (февраль 2005 г.). «Обзор адаптивной оптимизации виртуальных машин» (PDF) . Труды IEEE . 92 (2): 449–466. Архивировано из оригинала (PDF) 29 июня 2016 г.
  26. ^ Барретт и др. 2017, с. 3.
  27. ^ Трайни и др. 2022, с. 1.
  28. ^ Барретт и др. 2017, с. 5-6.
  29. ^ Барретт и др. 2017, с. 12-13.
  30. ^ Трайни и др. 2022, с. 17-23.
  31. ^ Трайни и др. 2022, с. 26-29.
  32. ^ «Как JIT – введение», Эли Бендерский, 5 ноября 2013 г., 5:59.
  33. ^ Де Муидж, январь. «JIT-код W^X включен в Firefox». Ян Де Муидж . Проверено 11 мая 2016 г.

Библиография

Внешние ссылки