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

Поделись знанием:
Перейти к: навигация, поиск

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

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

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





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

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

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

Описание

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

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

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

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

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

  1. Компиляция может осуществляться непосредственно для целевого процессора и операционной системы, на которой запущено приложение. Например, JIT может использовать векторные SSE2 расширения процессора, если он обнаружит их поддержку. Однако, до сих пор нет основных реализаций JIT, где этот подход бы использовался, ведь чтобы обеспечить подобный уровень оптимизации, сравнимый со статическими компиляторами, потребовалось бы либо поддерживать бинарный файл под каждую платформу, либо включать в одну библиотеку оптимизаторы под каждую платформу.
  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 году[5]. В его книге Recursive functions of symbolic expressions and their computation by machine, Part I он упоминает функции, компилируемые во время выполнения, тем самым избавив от надобности вывода работы компилятора на перфокарты.

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

Важный метод получения скомпилированного кода был предложен Митчелом в 1970 году, когда он реализовал экспериментальный язык LC2.[6][7]

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

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

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

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

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

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

См. также

Напишите отзыв о статье "JIT-компиляция"

Примечания

  1. Core Java: An Integrated Approach, p.12
  2. GNU lightning — GNU Project — Free Software Foundation (FSF)
  3. Benjamin Peterson — PyPy
  4. [habrahabr.ru/post/221937/ И снова про опасность eval()]
  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. Михаэль Франц — OberonCore
  10. Juice — OberonCore

Отрывок, характеризующий JIT-компиляция

Казак, сопутствовавший Несвицкому, подал сумочку и фляжку, и Несвицкий угощал офицеров пирожками и настоящим доппелькюмелем. Офицеры радостно окружали его, кто на коленах, кто сидя по турецки на мокрой траве.
– Да, не дурак был этот австрийский князь, что тут замок выстроил. Славное место. Что же вы не едите, господа? – говорил Несвицкий.
– Покорно благодарю, князь, – отвечал один из офицеров, с удовольствием разговаривая с таким важным штабным чиновником. – Прекрасное место. Мы мимо самого парка проходили, двух оленей видели, и дом какой чудесный!
– Посмотрите, князь, – сказал другой, которому очень хотелось взять еще пирожок, но совестно было, и который поэтому притворялся, что он оглядывает местность, – посмотрите ка, уж забрались туда наши пехотные. Вон там, на лужку, за деревней, трое тащут что то. .Они проберут этот дворец, – сказал он с видимым одобрением.
– И то, и то, – сказал Несвицкий. – Нет, а чего бы я желал, – прибавил он, прожевывая пирожок в своем красивом влажном рте, – так это вон туда забраться.
Он указывал на монастырь с башнями, видневшийся на горе. Он улыбнулся, глаза его сузились и засветились.
– А ведь хорошо бы, господа!
Офицеры засмеялись.
– Хоть бы попугать этих монашенок. Итальянки, говорят, есть молоденькие. Право, пять лет жизни отдал бы!
– Им ведь и скучно, – смеясь, сказал офицер, который был посмелее.
Между тем свитский офицер, стоявший впереди, указывал что то генералу; генерал смотрел в зрительную трубку.
– Ну, так и есть, так и есть, – сердито сказал генерал, опуская трубку от глаз и пожимая плечами, – так и есть, станут бить по переправе. И что они там мешкают?
На той стороне простым глазом виден был неприятель и его батарея, из которой показался молочно белый дымок. Вслед за дымком раздался дальний выстрел, и видно было, как наши войска заспешили на переправе.
Несвицкий, отдуваясь, поднялся и, улыбаясь, подошел к генералу.
– Не угодно ли закусить вашему превосходительству? – сказал он.
– Нехорошо дело, – сказал генерал, не отвечая ему, – замешкались наши.
– Не съездить ли, ваше превосходительство? – сказал Несвицкий.
– Да, съездите, пожалуйста, – сказал генерал, повторяя то, что уже раз подробно было приказано, – и скажите гусарам, чтобы они последние перешли и зажгли мост, как я приказывал, да чтобы горючие материалы на мосту еще осмотреть.
– Очень хорошо, – отвечал Несвицкий.
Он кликнул казака с лошадью, велел убрать сумочку и фляжку и легко перекинул свое тяжелое тело на седло.
– Право, заеду к монашенкам, – сказал он офицерам, с улыбкою глядевшим на него, и поехал по вьющейся тропинке под гору.
– Нут ка, куда донесет, капитан, хватите ка! – сказал генерал, обращаясь к артиллеристу. – Позабавьтесь от скуки.
– Прислуга к орудиям! – скомандовал офицер.
И через минуту весело выбежали от костров артиллеристы и зарядили.
– Первое! – послышалась команда.
Бойко отскочил 1 й номер. Металлически, оглушая, зазвенело орудие, и через головы всех наших под горой, свистя, пролетела граната и, далеко не долетев до неприятеля, дымком показала место своего падения и лопнула.
Лица солдат и офицеров повеселели при этом звуке; все поднялись и занялись наблюдениями над видными, как на ладони, движениями внизу наших войск и впереди – движениями приближавшегося неприятеля. Солнце в ту же минуту совсем вышло из за туч, и этот красивый звук одинокого выстрела и блеск яркого солнца слились в одно бодрое и веселое впечатление.


Над мостом уже пролетели два неприятельские ядра, и на мосту была давка. В средине моста, слезши с лошади, прижатый своим толстым телом к перилам, стоял князь Несвицкий.
Он, смеючись, оглядывался назад на своего казака, который с двумя лошадьми в поводу стоял несколько шагов позади его.
Только что князь Несвицкий хотел двинуться вперед, как опять солдаты и повозки напирали на него и опять прижимали его к перилам, и ему ничего не оставалось, как улыбаться.
– Экой ты, братец, мой! – говорил казак фурштатскому солдату с повозкой, напиравшему на толпившуюся v самых колес и лошадей пехоту, – экой ты! Нет, чтобы подождать: видишь, генералу проехать.
Но фурштат, не обращая внимания на наименование генерала, кричал на солдат, запружавших ему дорогу: – Эй! землячки! держись влево, постой! – Но землячки, теснясь плечо с плечом, цепляясь штыками и не прерываясь, двигались по мосту одною сплошною массой. Поглядев за перила вниз, князь Несвицкий видел быстрые, шумные, невысокие волны Энса, которые, сливаясь, рябея и загибаясь около свай моста, перегоняли одна другую. Поглядев на мост, он видел столь же однообразные живые волны солдат, кутасы, кивера с чехлами, ранцы, штыки, длинные ружья и из под киверов лица с широкими скулами, ввалившимися щеками и беззаботно усталыми выражениями и движущиеся ноги по натасканной на доски моста липкой грязи. Иногда между однообразными волнами солдат, как взбрызг белой пены в волнах Энса, протискивался между солдатами офицер в плаще, с своею отличною от солдат физиономией; иногда, как щепка, вьющаяся по реке, уносился по мосту волнами пехоты пеший гусар, денщик или житель; иногда, как бревно, плывущее по реке, окруженная со всех сторон, проплывала по мосту ротная или офицерская, наложенная доверху и прикрытая кожами, повозка.
– Вишь, их, как плотину, прорвало, – безнадежно останавливаясь, говорил казак. – Много ль вас еще там?
– Мелион без одного! – подмигивая говорил близко проходивший в прорванной шинели веселый солдат и скрывался; за ним проходил другой, старый солдат.
– Как он (он – неприятель) таперича по мосту примется зажаривать, – говорил мрачно старый солдат, обращаясь к товарищу, – забудешь чесаться.
И солдат проходил. За ним другой солдат ехал на повозке.
– Куда, чорт, подвертки запихал? – говорил денщик, бегом следуя за повозкой и шаря в задке.
И этот проходил с повозкой. За этим шли веселые и, видимо, выпившие солдаты.
– Как он его, милый человек, полыхнет прикладом то в самые зубы… – радостно говорил один солдат в высоко подоткнутой шинели, широко размахивая рукой.
– То то оно, сладкая ветчина то. – отвечал другой с хохотом.
И они прошли, так что Несвицкий не узнал, кого ударили в зубы и к чему относилась ветчина.
– Эк торопятся, что он холодную пустил, так и думаешь, всех перебьют. – говорил унтер офицер сердито и укоризненно.
– Как оно пролетит мимо меня, дяденька, ядро то, – говорил, едва удерживаясь от смеха, с огромным ртом молодой солдат, – я так и обмер. Право, ей Богу, так испужался, беда! – говорил этот солдат, как будто хвастаясь тем, что он испугался. И этот проходил. За ним следовала повозка, непохожая на все проезжавшие до сих пор. Это был немецкий форшпан на паре, нагруженный, казалось, целым домом; за форшпаном, который вез немец, привязана была красивая, пестрая, с огромным вымем, корова. На перинах сидела женщина с грудным ребенком, старуха и молодая, багроворумяная, здоровая девушка немка. Видно, по особому разрешению были пропущены эти выселявшиеся жители. Глаза всех солдат обратились на женщин, и, пока проезжала повозка, двигаясь шаг за шагом, и, все замечания солдат относились только к двум женщинам. На всех лицах была почти одна и та же улыбка непристойных мыслей об этой женщине.
– Ишь, колбаса то, тоже убирается!
– Продай матушку, – ударяя на последнем слоге, говорил другой солдат, обращаясь к немцу, который, опустив глаза, сердито и испуганно шел широким шагом.
– Эк убралась как! То то черти!
– Вот бы тебе к ним стоять, Федотов.
– Видали, брат!
– Куда вы? – спрашивал пехотный офицер, евший яблоко, тоже полуулыбаясь и глядя на красивую девушку.
Немец, закрыв глаза, показывал, что не понимает.
– Хочешь, возьми себе, – говорил офицер, подавая девушке яблоко. Девушка улыбнулась и взяла. Несвицкий, как и все, бывшие на мосту, не спускал глаз с женщин, пока они не проехали. Когда они проехали, опять шли такие же солдаты, с такими же разговорами, и, наконец, все остановились. Как это часто бывает, на выезде моста замялись лошади в ротной повозке, и вся толпа должна была ждать.
– И что становятся? Порядку то нет! – говорили солдаты. – Куда прешь? Чорт! Нет того, чтобы подождать. Хуже того будет, как он мост подожжет. Вишь, и офицера то приперли, – говорили с разных сторон остановившиеся толпы, оглядывая друг друга, и всё жались вперед к выходу.
Оглянувшись под мост на воды Энса, Несвицкий вдруг услышал еще новый для него звук, быстро приближающегося… чего то большого и чего то шлепнувшегося в воду.
– Ишь ты, куда фатает! – строго сказал близко стоявший солдат, оглядываясь на звук.
– Подбадривает, чтобы скорей проходили, – сказал другой неспокойно.
Толпа опять тронулась. Несвицкий понял, что это было ядро.
– Эй, казак, подавай лошадь! – сказал он. – Ну, вы! сторонись! посторонись! дорогу!
Он с большим усилием добрался до лошади. Не переставая кричать, он тронулся вперед. Солдаты пожались, чтобы дать ему дорогу, но снова опять нажали на него так, что отдавили ему ногу, и ближайшие не были виноваты, потому что их давили еще сильнее.
– Несвицкий! Несвицкий! Ты, г'ожа! – послышался в это время сзади хриплый голос.
Несвицкий оглянулся и увидал в пятнадцати шагах отделенного от него живою массой двигающейся пехоты красного, черного, лохматого, в фуражке на затылке и в молодецки накинутом на плече ментике Ваську Денисова.
– Вели ты им, чег'тям, дьяволам, дать дог'огу, – кричал. Денисов, видимо находясь в припадке горячности, блестя и поводя своими черными, как уголь, глазами в воспаленных белках и махая невынутою из ножен саблей, которую он держал такою же красною, как и лицо, голою маленькою рукой.
– Э! Вася! – отвечал радостно Несвицкий. – Да ты что?
– Эскадг'ону пг'ойти нельзя, – кричал Васька Денисов, злобно открывая белые зубы, шпоря своего красивого вороного, кровного Бедуина, который, мигая ушами от штыков, на которые он натыкался, фыркая, брызгая вокруг себя пеной с мундштука, звеня, бил копытами по доскам моста и, казалось, готов был перепрыгнуть через перила моста, ежели бы ему позволил седок. – Что это? как баг'аны! точь в точь баг'аны! Пг'очь… дай дог'огу!… Стой там! ты повозка, чог'т! Саблей изг'ублю! – кричал он, действительно вынимая наголо саблю и начиная махать ею.
Солдаты с испуганными лицами нажались друг на друга, и Денисов присоединился к Несвицкому.