Почему механизм выполнения LLVM быстрее скомпилированного кода? - PullRequest
44 голосов
/ 13 мая 2011

У меня есть компилятор, предназначенный для LLVM, и я предоставляю два способа запуска кода:

  1. Запустите его автоматически. Этот режим компилирует код в LLVM и использует JIT ExecutionEngine, чтобы скомпилировать его в машинный код на лету и запустить без создания выходного файла.
  2. Скомпилируйте его и запустите отдельно. В этом режиме выводится файл .bc LLVM, который я вручную оптимизирую (с opt), компилирую в собственную сборку (с llc), компилирую в машинный код и ссылку (с gcc) и запускаю.

Я ожидал, что заход на посадку № 2 будет быстрее, чем заход на посадку № 1 или, по крайней мере, такая же скорость, но, выполнив несколько тестов скорости, я с удивлением обнаружил, что № 2 постоянно работает примерно вдвое медленнее. Это огромная разница в скорости.

В обоих случаях используется один и тот же исходный код LLVM. С подходом № 1 я еще не удосужился выполнить какие-либо проходы оптимизации LLVM (именно поэтому я ожидал, что это будет медленнее). При подходе № 2 я запускаю opt с -std-compile-opts и llc с -O3, чтобы максимизировать оптимизацию, но она не приближается к # 1. Вот пример запуска той же программы:

  • # 1 без оптимизации: 11,833 с
  • # 2 без оптимизации: 22,262 с
  • # 2 с оптимизацией (-std-compile-opts и -O3): 18,823 с

Выполняет ли ExecutionEngine что-то особенное, о чем я не знаю? Можно ли как-нибудь оптимизировать скомпилированный код для достижения той же производительности, что и для ExecutionEngine JIT?

Ответы [ 2 ]

29 голосов
/ 01 июня 2011

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

Этот эффект также был замечен с виртуальными машинами Java и с Python PyPy VM, среди прочих.

14 голосов
/ 31 августа 2011

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

В качестве примера из реальной жизни давайте рассмотрим собственный клиент Google - я имею в виду оригинальный подход к компиляции nacl, не затрагивающий LLVM (потому что, насколько я знаю, в настоящее время существует руководство по поддержке как "nativeclient", так и "битового кода LLVM") (модифицированный) код).

Как вы можете видеть на презентациях (см. Youtube.com) или в документах , например, Собственный клиент: песочница для переносимого ненадежного собственного кода x86 , даже их выравнивание Техника увеличивает размер кода, в некоторых случаях такое выравнивание команд (например, с помощью noops) дает лучшее попадание в кеш.

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

Я надеюсь, что этот ответ дает представление о том, как обстоятельства могут повлиять на скорость выполнения кода, и это множество возможных причин для разных частей кода, и каждый из них требует изучения. Никогда, это интересная тема, поэтому, если вы найдете более подробную информацию, не стесняйтесь переиздать свой ответ и дайте нам знать в "Post-Scriptorium", что вы нашли больше :). (Может быть, ссылка на официальный документ / devblog с новыми находками :)). Тесты всегда приветствуются - взгляните: http://llvm.org/OpenProjects.html#benchmark.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...