stringtranslate.com

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

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

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

История

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

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

Self был заброшен Sun, но исследования перешли на язык Java. Термин «Just-in-time compilation» был заимствован из производственного термина « Just-in-time » и популяризирован Java, Джеймс Гослинг использовал этот термин с 1993 года. [11] В настоящее время JITing используется большинством реализаций виртуальной машины 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]

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

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

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

Существуют также реализации Java, которые объединяют компилятор AOT (ahead-of-time) с JIT-компилятором ( Excelsior JET ) или интерпретатором ( GNU Compiler for 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.12.2018 .
  4. ^ ab Aycock 2003, 2. Методы компиляции JIT, 2.1 Genesis, стр. 98.
  5. ^ Маккарти, Дж. (апрель 1960 г.). «Рекурсивные функции символических выражений и их вычисление машиной, часть I». Сообщения ACM . 3 (4): 184–195. CiteSeerX 10.1.1.111.8833 . doi :10.1145/367177.367199. S2CID  1489409. 
  6. ^ Томпсон 1968.
  7. ^ Aycock 2003, 2. Методы компиляции JIT, 2.2 LC², стр. 98–99.
  8. ^ Митчелл, Дж. Г. (1970). «Проектирование и создание гибких и эффективных интерактивных систем программирования». {{cite journal}}: Цитировать журнал требует |journal=( помощь )
  9. ^ Deutsch, LP; Schiffman, AM (1984). "Эффективная реализация системы smalltalk-80" (PDF) . Труды 11-го симпозиума ACM SIGACT-SIGPLAN по принципам языков программирования - POPL '84 . стр. 297–302. doi :10.1145/800017.800542. ISBN 0-89791-125-3. S2CID  3045432. Архивировано из оригинала (PDF) 2004-06-18.
  10. ^ "97-pep.ps". research.sun.com . Архивировано из оригинала 24 ноября 2006 . Получено 15 января 2022 .
  11. ^ Aycock 2003, 2.14 Java, стр. 107, сноска 13.
  12. ^ "Dynamo: A Transparent Dynamic Optimization System". Васант Бала, Эвелин Дюстервальд, Санджив Банерджиа. Труды конференции PLDI '00 ACM SIGPLAN 2000 по проектированию и реализации языков программирования. Страницы с 1 по 12. DOI 10.1145/349299.349303. Получено 28 марта 2012 г.
  13. ^ Джон Джаннотти. "HP's Dynamo". Ars Technica . Получено 05.07.2013 .
  14. ^ "Проект HP Dynamo". Архивировано из оригинала 19 октября 2002 г. Получено 12 апреля 2016 г.{{cite web}}: CS1 maint: unfit URL (link)
  15. ^ Tung, Liam (27 ноября 2020 г.). «Язык программирования PHP 8 вышел: этот новый JIT-компилятор указывает на лучшую производительность». ZDNet . Получено 28 ноября 2020 г.
  16. ^ Кроче, Луис. "Just in Time Compilation" (PDF) . Колумбийский университет . Архивировано из оригинала (PDF) 2018-05-03.
  17. ^ "Каковы преимущества JIT-компиляции по сравнению с AOT-компиляцией". Stack Overflow . 21 января 2010 г.
  18. ^ "Компилировать язык на основе JIT в Webassembly". Stack Overflow . Получено 2018-12-04 .
  19. ^ Хаазе, Чет (май 2007 г.). «Потребительская JRE: более компактная и скромная технология Java». Sun Microsystems . Получено 27 июля 2007 г.
  20. ^ "Архитектура Java HotSpot Performance Engine". Oracle.com . Получено 2013-07-05 .
  21. ^ Шиллинг, Джонатан Л. (февраль 2003 г.). «Простейшие эвристики могут быть лучшими в JIT-компиляторах Java» (PDF) . Уведомления SIGPLAN . 38 (2): 36–46. doi :10.1145/772970.772975. S2CID  15117148. Архивировано из оригинала (PDF) 24.09.2015.
  22. ^ Тосио Суганума, Тосиаки Ясуэ, Мотохиро Кавахито, Хидеаки Комацу, Тосио Накатани, «Динамическая оптимизационная структура для Java-компилятора just-in-time», Труды 16-й конференции ACM SIGPLAN по объектно-ориентированному программированию, системам, языкам и приложениям (OOPSLA '01), стр. 180–195, 14–18 октября 2001 г.
  23. ^ Мэтью Арнольд, Майкл Хайнд, Барбара Г. Райдер, «Эмпирическое исследование выборочной оптимизации», Труды 13-го Международного семинара по языкам и компиляторам для параллельных вычислений — пересмотренные статьи , стр. 49–67, 10–12 августа 2000 г.
  24. ^ "Native Image Generator (Ngen.exe)". Msdn2.microsoft.com. 5 декабря 2006 г. Получено 2013-07-05 .
  25. ^ Суини, Арнольд (февраль 2005 г.). «Обзор адаптивной оптимизации в виртуальных машинах» (PDF) . Труды IEEE . 92 (2): 449–466. Архивировано из оригинала (PDF) 29-06-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 г.

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

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