JIT-компилятор для C, C ++ и тому подобное

33

Есть ли компилятор точно в срок для скомпилированных языков, таких как C и C ++? (Первые имена, которые приходят на ум, это Clang и LLVM! Но я не думаю, что они в настоящее время поддерживают это.)

Объяснение:

Я думаю, что программное обеспечение могло бы выиграть от обратной связи профилирования во время выполнения и агрессивно оптимизированной перекомпиляции горячих точек во время выполнения, даже для скомпилированных в машину языков, таких как C и C ++.

Оптимизация по профилю выполняет аналогичную работу, но с той разницей, что JIT будет более гибким в разных средах. В PGO вы запускаете свой бинарный файл до его выпуска. После того, как вы выпустили его, он не будет использовать обратную связь среды / ввода, собранную во время выполнения. Таким образом, если шаблон ввода изменяется, это является пробой к снижению производительности. Но JIT хорошо работает даже в таких условиях.

Однако я считаю, что это спорный кастрированный баран ЛТ компиляции выигрыш в производительности перевешивает свои собственные накладные расходы.

Эбрахим Мохаммади
источник
1
Не по теме - вне сайта.
DeadMG
1
Не уверен, что он подходит для вопроса, но для удобства использования я считаю полезным пакет Cxx на языке Julia ... он дает вам интерактивное приглашение C ++, подобное описанному в ответе @ PhilippClaßen.
Антонелло
GCC 9 теперь имеет jit-компилятор gcc.gnu.org/onlinedocs/jit/intro/index.html
user3071643

Ответы:

33

[Смотрите историю изменений для совершенно другого ответа, который сейчас в основном устарел.]

Да, есть пара JIT-компиляторов для C и / или C ++.

CLing (как вы можете догадаться из игры) основан на Clang / LLVM. Он действует как переводчик. То есть вы даете ему некоторый исходный код, даете команду для его запуска, и он запускается. Основное внимание здесь уделяется удобству и быстрой компиляции, а не максимальной оптимизации. Таким образом, хотя технически это и есть ответ на сам вопрос, это на самом деле не очень подходит для намерения ОП.

Другая возможность - NativeJIT . Это подходит к вопросу несколько иначе. В частности, он не принимает исходный код C или C ++, а компилирует и выполняет его. Скорее, это небольшой компилятор, который вы можете скомпилировать в вашу C ++ программу. Он принимает выражение, которое в основном выражается как EDSL внутри вашей программы на C ++, и генерирует из этого фактический машинный код, который вы затем можете выполнить. Это намного лучше подходит для фреймворка, в котором вы можете скомпилировать большую часть своей программы с помощью обычного компилятора, но у вас есть несколько выражений, о которых вы не узнаете до времени выполнения, которые вы хотите выполнить с тем, что приближается к оптимальной скорости выполнения.

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

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

Джерри Гроб
источник
1
Почему JIT-файлы не сохраняют файл, похожий на кеш, чтобы они могли пропустить повторное изучение всего с нуля?
JohnMudd
3
@JohnMudd: я подозреваю, что причина - безопасность. Например, измените кэшированный код, а затем при следующем запуске виртуальной машины она выполнит код, который я поместил туда вместо того, что он там написал.
Джерри Гроб
4
OTOH, если вы можете изменить кэши, вы также можете изменить исходные файлы.
user3125367
1
@ user3125367: Да, но во многих случаях компилятор выполняет различную проверку типов и такую, которую можно обойти, если вы загружаете скомпилированный код непосредственно из кэша. Конечно, зависит от JIT - Java выполняет большую принудительную работу при загрузке (скомпилированного) файла .class, но многие другие делают намного меньше (во многих случаях почти ничего).
Джерри Гроб
11

Да, есть JIT-компиляторы для C ++. С точки зрения производительности, я думаю, что Оптимизация по профилю (PGO) все еще лучше.

Однако это не означает, что JIT-компиляция еще не используется на практике. Например, Apple использует LLVM в качестве JIT для своего конвейера OpenGL. Это домен, где у вас есть значительно больше информации во время выполнения, которую можно использовать для удаления большого количества мертвого кода.

Еще одним интересным приложением JIT является Cling, интерактивный интерпретатор C ++, основанный на LLVM и Clang: https://root.cern.ch/cling

Вот пример сеанса:

[cling]$ #include <iostream>
[cling]$ std::cout << "Hallo, world!" << std::endl;
Hallo, world!
[cling]$ 3 + 5
(int const) 8
[cling]$ int x = 3; x++
(int) 3
(int const) 3
[cling]$ x
(int) 4

Это не игрушечный проект, но он фактически используется в CERN, например, для разработки кода для Большого адронного коллайдера.

Филипп Классен
источник
7

C ++ / CLI соединяется. Конечно, C ++ / CLI не C ++, но он довольно близок. Тем не менее, Microsoft JIT не делает супер умных / симпатичных оптимизаций на основе поведения во время выполнения, о которых вы спрашиваете, по крайней мере, насколько мне известно. Так что это действительно не помогает.

http://nestedvm.ibex.org/ превращает MIPS в байт-код Java, который затем соединяется. Проблема с этим подходом из вашего вопроса состоит в том, что вы выбрасываете много полезной информации к тому времени, когда она попадает в JIT.

Логан Капальдо
источник
2

Во-первых, я предполагаю, что вы хотите использовать jit для трассировки, а не метод jit.

Лучшим подходом было бы скомпилировать код в llvm IR, затем добавить код трассировки, прежде чем создавать собственный исполняемый файл. Как только блок кода становится достаточно хорошо используемым, и как только достаточно информации о значениях (а не о типах, как в динамических языках) переменных, собирается, код может быть перекомпилирован (из IR) со средствами защиты, основанными на значениях переменных.

Кажется, я помню, что был достигнут некоторый прогресс в создании ac / c ++ jit в clang под именем libclang.

dan_waterworth
источник
1
AFAIK, libclang - это большая часть функциональности clang, разработанной как библиотека. Таким образом, вы можете использовать его для анализа исходного кода для создания сложной окраски синтаксиса, ссылок, просмотра кода и т. д.
Хавьер
@ Хавьер, это звучит правильно. Я думаю, что в библиотеке была функция, которая взяла const char * исходного кода и произвела llvm ir, но теперь я думаю, что лучше jit, основываясь на ir, чем на источнике.
dan_waterworth