Interested Article - JIT-компиляция

JIT-компиляция ( англ. Just-in-Time , компиляция «точно в нужное время»), динамическая компиляция ( англ. dynamic translation ) — технология увеличения производительности программных систем, использующих байт-код , путём компиляции байт-кода в машинный код или в другой формат непосредственно во время работы программы. Таким образом достигается высокая скорость выполнения по сравнению с интерпретируемым байт-кодом (сравнимая с компилируемыми языками) за счёт увеличения потребления памяти (для хранения результатов компиляции) и затрат времени на компиляцию. Технология JIT базируется на двух более ранних идеях, касающихся среды выполнения: компиляции байт-кода и динамической компиляции .

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

Проекты LLVM , GNU Lightning , libJIT (часть проекта DotGNU ) и RPython (часть проекта PyPy ) могут быть использованы для создания JIT-интерпретаторов любого скриптового языка.

Особенности реализации

JIT-компиляция может быть применена как ко всей программе, так и к её отдельным частям. Например, текстовый редактор может на лету компилировать регулярные выражения для более быстрого поиска по тексту. С AOT-компиляцией такое сделать не представляется возможным для случаев, когда данные предоставляются во время исполнения программы, а не в момент компиляции. JIT используется в реализациях Java (JRE), JavaScript , .NET Framework , в одной из реализаций Python — PyPy . Существующие наиболее распространённые интерпретаторы языков PHP , Ruby , Perl , Python и им подобных имеют ограниченные или неполные JIT.

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

Описание

В языках, таких как Java , PHP , C# , Lua , Perl , GNU CLISP , исходный код транслируется в одно из промежуточных представлений , называемое байт-кодом . Байт-код не является машинным кодом какого-либо конкретного процессора и может переноситься на различные компьютерные архитектуры и исполняться точно так же. Байт-код интерпретируется (исполняется) виртуальной машиной . JIT читает байт-код из некоторых секторов (редко сразу из всех) и компилирует их в машинный код. Этим сектором может быть файл, функция или любой фрагмент кода. Единожды скомпилированный код может кэшироваться и в дальнейшем повторно использоваться без перекомпиляции.

Динамически компилируемая среда — это среда, в которой компилятор может вызываться приложением во время выполнения. Например, большинство реализаций Common Lisp содержат функцию compile , которая может создать функцию во время выполнения; в Python это функция eval . Это удобно для программиста, так как он может контролировать, какие части кода действительно подлежат компиляции. Также с помощью этого приёма можно компилировать динамически сгенерированный код, что в некоторых случаях приводит даже к лучшей производительности, чем реализация в статически скомпилированном коде. Однако стоит помнить, что подобные функции могут быть опасны, особенно когда данные передаются из недоверенных источников.

Основная цель использования JIT — достичь и превзойти производительность статической компиляции, сохраняя при этом преимущества динамической компиляции:

  • Большинство тяжеловесных операций, таких, как парсинг исходного кода и выполнение базовых оптимизаций, происходит во время компиляции (до развёртывания), в то время как компиляция в машинный код из байт-кода происходит быстрее, чем из исходного кода.
  • Байт-код более переносим (в отличие от машинного кода).
  • Среда может контролировать выполнение байт-кода после компиляции, поэтому приложение может быть запущено в песочнице (для нативных программ такая возможность тоже существует, но реализация данной технологии сложнее).
  • Компиляторы из байт-кода в машинный код легче в реализации, так как большинство работы по оптимизации уже было проделано компилятором.

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

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

Задержка при запуске, средства борьбы с ней

Типичная причина задержки при запуске JIT-компилятора — расходы на загрузку среды и компиляцию приложения в машинный код. В общем случае, чем лучше и чем больше оптимизаций выполняет JIT, тем больше получается задержка. Поэтому разработчикам JIT приходится искать компромисс между качеством генерируемого кода и временем запуска. Однако, часто оказывается так, что узким местом в процессе компиляции оказывается не сам процесс компиляции, а задержки системы ввода-вывода (так, например, rt.jar в Java Virtual Machine (JVM) имеет размер 40 МБ, и поиск метаданных в нём занимает достаточно большое количество времени).

Ещё одно средство оптимизации — компилировать только те участки приложения, которые используются чаще всего. Этот подход реализован в PyPy и HotSpot Java Virtual Machine компании Sun Microsystems .

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

Порой достаточно сложно найти правильный компромисс. Так, например, Sun’s Java Virtual Machine имеет два режима работы — клиент и сервер. В режиме клиента количество компиляций и оптимизаций минимально для более быстрого запуска, в то время как в режиме сервера достигается максимальная производительность, но из-за этого увеличивается время запуска.

Ещё одна техника, называемая pre-JIT, компилирует код до запуска. Преимуществом данной техники является уменьшенное время запуска, в то же время недостатком является плохое качество скомпилированного кода по сравнению с runtime JIT.

История

Самую первую реализацию JIT можно отнести к LISP, написанную McCarthy в 1960 году . В его книге Recursive functions of symbolic expressions and their computation by machine, Part I он упоминает функции, компилируемые во время выполнения, тем самым избавив от надобности вывода работы компилятора на перфокарты .

Другой ранний пример упоминания JIT можно отнести к Кену Томпсону , который в 1968 году впервые применил регулярные выражения для поиска подстрок в текстовом редакторе QED . Для ускорения алгоритма Томпсон реализовал компиляцию регулярных выражений в машинный код IBM 7094 .

Метод получения скомпилированного кода был предложен Митчелом в 1970 году , когда он реализовал экспериментальный язык LC 2 .

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

Self был заброшен Sun, но исследования продолжились в рамках языка Java. Термин «Just-in-time компиляция» был заимствован из производственного термина «Точно в срок» и популяризован Джеймсом Гослингом , использовавшим этот термин в 1993. В данный момент JIT используется почти во всех реализациях Java Virtual Machine.

Также большой интерес представляет диссертация, защищённая в 1994 году в Университете ETH (Швейцария, Цюрих) Михаэлем Францем «Динамическая кодогенерация — ключ к переносимому программному обеспечению» и реализованная им система Juice динамической кодогенерации из переносимого семантического дерева для языка Оберон . Система Juice предлагалась как плагин для интернет-браузеров.

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

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

JIT компиляция включает в себя компиляцию исходного кода или байт-кода в машинный код и его выполнение. Как правило, результат записывается в память и исполняется сразу же, без промежуточного сохранения на диск или его вызов как отдельной программы. В современных архитектурах для повышения безопасности произвольные участки памяти как машинный код ( NX bit ). Для корректного запуска регионы памяти должны быть предварительно помечены как исполняемые, при этом для большей безопасности флаг исполнения может ставиться только после снятия флага разрешения записи (Схема защиты W^X) .

См. также

Примечания

  1. от 27 августа 2017 на Wayback Machine , 2008, ISBN 9788177228366 , Dreamtech Press, 2008. p.12
  2. . Дата обращения: 27 августа 2017. 19 сентября 2017 года.
  3. Benjamin Peterson — от 12 мая 2008 на Wayback Machine
  4. от 13 сентября 2014 на Wayback Machine , habrahabr
  5. Aycock 2003, 2. JIT Compilation Techniques, 2.1 Genesis, p. 98.
  6. Aycock 2003, 2. JIT Compilation Techniques, 2.2 LC², p. 98-99.
  7. Mitchell, J.G. (1970). The design and construction of flexible and efficient interactive programming systems .
  8. Aycock & 2003 2.14 Java, p. 107, footnote 13.
  9. от 26 сентября 2017 на Wayback Machine ; от 7 сентября 2017 на Wayback Machine
  10. . Дата обращения: 7 ноября 2009. 23 декабря 2009 года.
  11. от 2 августа 2017 на Wayback Machine / Phoronix, 4 January 2016 (англ.)


Источник —

Same as JIT-компиляция