LLVM и будущее оптимизации
Я понимаю, что LLVM имеет долгий путь, но теоретически, могут быть оптимизации, которые находятся в GCC/ICC/etc. для отдельных языков применяется к байт-коду LLVM? Если да, значит ли это, что любой язык, который компилируется в байт-код LLVM, может быть столь же быстрым? Или это языковые оптимизации (до этапа байт-кода LLVM), которые всегда будут играть большую роль в оптимизации любой конкретной программы.
Я не знаю много о компиляторах или оптимизации (достаточно, чтобы быть опасным), поэтому я приношу свои извинения, если этот вопрос не определен.
Ответы
Ответ 1
В общем, нет.
Например, в Haskell общей оптимизацией является анализ строгости, который позволяет компилятору определять, какие переменные всегда в форме головы, и поэтому может быть принудительно + встроен без изменения семантики программы. Это невозможно в LLVM.
Объяснение: В Haskell функция (Int, Int) -> Int
более или менее эквивалентна типу в C:
typedef int (*returns_int)();
struct pair { returns_int first, second};
typedef struct pair *(*returns_pair)();
int function(returns_pair arg);
Компилятор может анализировать function
и определять, что он всегда оценивает свой аргумент и всегда извлекает содержимое, превращая функцию в это:
int function(int x, int y); // note that it takes *two* arguments now
Это намного превосходит возможности LLVM. Возможно, в будущем, с некоторой действительно тяжелой межпроцедурной оптимизацией... но реалистично говоря, этого не произойдет в обозримом будущем.
Пример 2: Существуют виртуальные машины Java, которые могут преобразовывать вызовы виртуальных функций в прямые вызовы функций. Однако это не то, что LLVM может сделать, потому что это преобразование должно быть отменено динамически, если загружается другой класс, который реализует один и тот же интерфейс.
В общем случае при компиляции программы в LLVM вы теряете большую часть семантической информации об исходной программе. Байт-код LLVM способен представлять любой код, но его система типов довольно ограничена - и ваш выбор системы типов влияет на то, какие оптимизации вы можете сделать.
Ответ 2
В дополнение к Дитриху отличный ответ, я думаю, важно понимать, что не только компилятор определяет, насколько быстрыми являются языки программирования. Помимо различных оптимизаций, которые может разрешить/запретить данный язык, существует также вопрос о том, как вы выполняете определенные задачи на разных языках программирования и что язык позволяет вам делать.
Например, относительно легко оптимизировать код C, чтобы максимизировать эффективность кеша (уменьшая медленное чтение из памяти), в то время как в Haskell это намного сложнее. В Java невозможно использовать хакеры-указатели. Как и стратегия выделения огромного куска памяти и разбора ее вручную.
Таким образом, некоторые языки всегда будут медленнее, просто потому, что они не допускают одинакового уровня оптимизации. Обратите внимание, что я не обязательно говорю, что это плохо, потому что с этой медлительностью появляются чрезвычайно мощные конструкции.
Я думаю, что лучший способ взглянуть на это - это то, что LLVM позволит использовать определенный набор оптимизаций для всех языков, которые скомпилируются до него. Таким образом, хотя он будет делать такие языки быстрее, он не сделает их одинаково быстрыми.
Изменить: ручка указателя в Haskell. Так много возможностей...
Ответ 3
Это интересный вопрос, но я боюсь, что вам не хватает некоторых представлений о том, что делают компиляторы.
В компиляторе всегда будет несколько этапов оптимизации.
- Некоторые оптимизации зависят от правил языков, например, на С++ вы можете оптимизировать некоторые вызовы конструктора-конструктора копирования/перемещения.
- Некоторые оптимизации широко доступны (преобразования цикла, хвостовые вызовы)
- Некоторые оптимизации зависят от аппаратного обеспечения (SSE, MMX)
Компилятор LLVM применяет все 3... но начиная с LLVM IR, а не от C или Java или от других.
Это задача front-end для обеспечения подходящего IRL-LLVM.
Например, как отмечает @Dietrich Epp, IR не подходит для функциональных языков. Поэтому многие оптимизации Haskell должны быть выполнены до того, как представление будет уменьшено до IR.
Другим источником не оптимизации является то, что определенная среда выполнения может поставляться с языками. Haskell имеет сложную среду выполнения с пулом sparks, легкими потоками, преемством перед системными вызовами, кражей работы и т.д. IR не подходит для представления этой богатой среды, и в этой организации не делается оптимизация.
Ответ 4
LLVM значительно продвинулась вперед, чтобы стать перспективной альтернативой GCC (даже скомпилировать ядро Linux с некоторыми исправлениями). Это также во многих случаях быстрее, чем GCC (компилятор и сгенерированные исполняемые файлы) и имеет структуру, которая упрощает запись интерфейсов для произвольных языков.
Но чтобы обеспечить широкие оптимизации, внешний интерфейс не менее важен, так как он знает гораздо больше информации о компиляции программы. Вещи, которые стек LLVM не может легко узнать. Поэтому для создания эффективных программ также интерфейс должен оптимизировать код.
Ответ 5
Возвращаясь к вашему вопросу , возможно ли это теоретически? ",
пусть представьте/предположим - просто ради обсуждения - следуйте за:
- У нас есть байт-код или даже машинный код
- мы знаем, что он создан с данным компилятором X
- исходным языком был L
- что программа ввода имела конечный размер.
ИМХО - с точки зрения компьютерной науки, это в основном вопрос о ресурсах, применять практически все.
Теперь попробуйте выполнить
function machinecode_to_sourcecode( given_machinecode ){
it = all_posibble_strings_iterator()
while (true) {
possible_sourcecode = it->get_string()
machine_code = compile possible_sourcecode with X
if (compilation succeeded)
if(machine_code == given_machinecode)
return possible_sourcecode;
else
it = it->next_string()
}
}
Итак, мы пытаемся использовать все возможные строки, как входные данные для компилятора X, в случае компиляции успеха - результаты равны, у нас есть источник.
Как только у нас есть источник, у нас есть как можно больше информации о программе, поэтому мы можем применить все возможные оптимизации.
Все выглядит очень дорого, но, как вы спросили "теоретически", я скажу
- потребуется конечное время
потому что
- исходный код ввода имеет конечную длину
так
- итерация при всех таких строках занимает конечное время
- Я предположил, что компилятор обрабатывает любой исходный код за конечное время (это то, что мы обычно предполагаем, когда думаем о компиляторе;)).
вся операция будет занимать конечное количество времени вычисления.
Ответ 6
Я не знаю никаких подробностей о формате байт-кода, используемом LLVM, но я думаю, что ответ на ваш вопрос - нет.
Просто рассмотрите следующее: динамическое и статическое типирование. Язык программирования, который динамически типизирован, вероятно, будет медленнее, чем статически типизированный язык, потому что большая часть проверки типов выполняется во время выполнения.
Также могут быть некоторые другие различия между языками программирования, которые могут повлиять на производительность.
Ответ 7
Здесь я нашел отчет, с легким для начинающих обзором различий GCC и LLVM:
Отчет по оценке зрелости Clang/LLVM Доминика Фандри