Подтвердить что ты не робот

Оптимизация времени статических языков: JIT для С++?

Кто-нибудь использует трюки JIT для улучшения производительности во время статических компилируемых языков, таких как С++? Похоже, что анализ хот-спота и прогнозирование ветвлений на основе наблюдений, сделанных во время выполнения, могут улучшить производительность любого кода, но, возможно, существует фундаментальная стратегическая причина, по которой такие наблюдения и реализации изменений во время выполнения возможны только на виртуальных машинах. Я отчетливо помню, что слышал, как компиляторы С++ проговаривают "вы можете сделать это для программ, написанных на С++", а при прослушивании динамических энтузиастов языка говорят о сборе статистики и переупорядочении кода, но мои поисковые запросы в Интернете для поддержки этой памяти вышли из строя.

4b9b3361

Ответ 1

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

Вам может быть интересно узнать информацию о HP Dynamo, хотя эта система ориентирована на собственный бинарный → родной двоичный перевод, хотя с С++ почти исключительно скомпилирован в собственный код. Я полагаю, что именно то, что вы ищете.

Вы также можете взглянуть на LLVM, который представляет собой структуру компилятора и промежуточное представление, которое поддерживает компиляцию JIT и оптимизацию времени выполнения, хотя я не уверен, что на самом деле есть какие-то временные среды на основе LLVM, которые могут скомпилировать С++ и выполнить + runtime еще оптимизировать ее.

Ответ 2

Я делал такую ​​оптимизацию довольно много за последние годы. Это было для API графического рендеринга, который я реализовал. Поскольку API определил несколько тысяч различных режимов рисования, поскольку функция общего назначения была способ замедления.

В итоге я написал свой собственный Jit-компилятор для языка, специфичного для домена (очень близко к asm, но с некоторыми структурами управления высокого уровня и локальными переменными).

Улучшение производительности, которое я получил, составляло от 10 до 60 (зависит от сложности скомпилированного кода), поэтому дополнительная работа окупилась большим временем.

На ПК я бы не стал писать собственный jit-компилятор, но использовал LIBJIT или LLVM для jit-компиляции. Это было невозможно в моем случае из-за того, что я работал над встроенным встроенным процессором, не поддерживаемым LIBJIT/LLVM, поэтому мне пришлось изобретать свои собственные.

Ответ 3

Ответ более вероятен: никто не делал больше, чем PGO для С++, потому что преимущества, вероятно, незаметны.

Позвольте мне уточнить: двигатели JIT/время автономной работы имеют как благословения, так и недостатки от их представления разработчика: у них больше информации во время выполнения, но очень мало времени для анализа. Некоторые оптимизации действительно дороги, и вы вряд ли увидите без значительного влияния на время запуска, такие как: циклическая развертка, автоматическая векторизация (которая в большинстве случаев также основана на разворачивании цикла), выбор команды (для использования SSE4.1 для CPU, использующий SSE4.1) в сочетании с планированием и переупорядочением команд (для использования лучших суперскалярных ЦП). Такая оптимизация отлично сочетается с C-кодом (который доступен из С++).

Единая полномасштабная архитектура компилятора для выполнения передовой компиляции (насколько мне известно) представляет собой сборку и архитектуру Java Hotspot с аналогичными принципами с использованием многоуровневой компиляции (системы Java Azul, популярный в течение дня механизм JegerMonkey JS).

Но одна из самых больших оптимизаций во время выполнения:

Полиморфное встроенное кэширование (это означает, что если вы запускаете первый цикл с некоторыми типами, то во второй раз код цикла будет специализированным типом, который был из предыдущего цикла, и JIT будет помещать охранник и будет по умолчанию использовать встроенные типы, и на основе этого из этой специализированной формы с использованием механизма SSA-формы будет применяться постоянная сворачивание/распространение, встраивание, оптимизация исключений из мертвого кода и зависит от того, как "продвинутый" JIT, будет делать улучшенное или менее улучшенное назначение регистра CPU.) Как вы можете заметить, JIT (hotspots) улучшит в основном разветвленный код, а информация о времени выполнения будет лучше, чем С++-код, но статический компилятор, имея на нем время для проведения анализа, переупорядочения команд для простых циклов, скорее всего, немного улучшится. Кроме того, как правило, код С++, области, которые должны быть быстрыми, как правило, не являются ООП, поэтому информация об оптимизации JIT не принесет такого удивительного улучшения.

Другим преимуществом JIT является то, что JIT работает с перекрестными сборками, поэтому у него есть больше информации, если он хочет сделать inlining.

Позвольте мне уточнить: скажем, что у вас есть базовый класс A, и у вас есть реализация только одна, а именно B в другом пакете/сборке/gem/etc. и загружается динамически.

JIT, поскольку он видит, что B является единственной реализацией A, он может повсеместно заменять внутри себя внутреннее представление вызовов A с B-кодами, а вызовы метода не будут отправлять диспетчеризацию (смотрите на vtable), но будут прямыми звонки. Эти прямые вызовы также могут быть включены. Например, у этого метода B есть метод: getLength(), который возвращает 2, все вызовы getLength() могут быть сведены к константе 2 на всем протяжении. В конце код С++ не сможет пропустить виртуальный вызов B из другой dll.

Некоторые реализации С++ не поддерживают оптимизацию более .cpp файлов (даже сегодня в последних версиях GCC есть флаг -lto, который делает это возможным). Но если вы разработчик С++, обеспокоенный скоростью, вы, скорее всего, ставите все чувствительные классы в одну и ту же статическую библиотеку или даже в один и тот же файл, поэтому компилятор может легко вставить его, добавив дополнительную информацию, которую JIT имеет по дизайну, которые должны предоставляться самим разработчиком, поэтому потери производительности не выполняются.

Ответ 4

визуальная студия имеет возможность выполнения профилирования во время выполнения, которая затем может использоваться для оптимизации кода.

"Профилированная оптимизация"

Ответ 5

Microsoft Visual Studio называет эту " оптимизацию, управляемую профилем"; вы можете узнать больше об этом на MSDN. В основном, вы запускаете программу несколько раз с профилировщиком, прикрепленным для записи своих горячих точек и других характеристик производительности, а затем вы можете подать выход профилировщика в компилятор, чтобы получить соответствующую оптимизацию.

Ответ 6

Я верю, что LLVM пытается сделать что-то из этого. Он пытается оптимизировать всю жизнь программы (время компиляции, время ссылки и время выполнения).

Ответ 7

Разумный вопрос - но с сомнительной предпосылкой.

Как и в ответе Нилса, иногда "оптимизация" означает "оптимизация на низком уровне", что является хорошим предметом по своему усмотрению.

Однако он основан на концепции "горячего пятна", которая не имеет почти никакой актуальности, которую она обычно дает.

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

Если есть горячая точка, такая как плотный внутренний цикл, занимающий много времени, стоит попытаться оптимизировать на низком уровне, если он находится в коде, который вы контролируете (т.е. не в стороннем библиотека).

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

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

Смотрите это.