Michael Litvinov
CPP RUSSIA 2025 speaker
Применение ML в компиляторах
Литвинов Михаил
@litvinovmitch11
Выполнил работу:
@kpdev42
Ментор:
Косов Павел
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
// Initialize targets for clang module support.
llvm::InitializeAllTargets();
auto ExpectedParser =
CommonOptionsParser::create(argc, argv, ClangCheckCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
...
}ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0xe143f0
Start of program headers: 64 (bytes into file)
...ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0xe143f0
Start of program headers: 64 (bytes into file)
...int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
// Initialize targets for clang module support.
llvm::InitializeAllTargets();
auto ExpectedParser =
CommonOptionsParser::create(argc, argv, ClangCheckCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
...
}ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0xe143f0
Start of program headers: 64 (bytes into file)
...int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
// Initialize targets for clang module support.
llvm::InitializeAllTargets();
auto ExpectedParser =
CommonOptionsParser::create(argc, argv, ClangCheckCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
...
}С, C++, Rust, ...
LLVM IR
Program analysis
Transform & optimize
CodeGen
ARM, X86, ...
С++
LLVM IR
INFO
OPT
CodeGen
ARM, X86
Pipelines (O1-O3, Os, Oz)
Простейший вид CFG
Control Flow Graph
Граф потока управления
Простейший вид CFG
Control Flow Graph
Граф потока управления
Заметим, что в NODE №3 никак не попасть
Простейший вид CFG
Control Flow Graph
Граф потока управления
Заметим, что в NODE №3 никак не попасть - ее можно не генерировать.
Простейший вид CFG
Control Flow Graph
Граф потока управления
- Упростили граф
- Уменьшили codesize
- Уменьшили время компиляции
В компиляторе большое число (>100) независимых трансформаций.
От порядка выполнения трансформаций зависит результат.
PASS 1
PASS 2
PASS 3
PASS 4
PASS 1
PASS 2
PASS 3
PASS 4
PASS 1
PASS 2
PASS 3
PASS 4
PASS 1
PASS 2
PASS 3
PASS 4
Выбор наилучшей последовательности - NP полная задача.
Существуют дефолтные пайплайны, написанные экспертами, но они хороши лишь В СРЕДНЕМ.
На помощь приходит RL!
PASS 1
PASS 2
PASS 3
PASS 4
PASS 1
PASS 2
PASS 3
PASS 4
PASS 1
PASS 2
PASS 3
PASS 4
PASS 1
PASS 2
PASS 3
PASS 4
for (size_t i = 0; i < n; ++i) {
f(i, v);
}Инлайнинг - встраивание тела функции, вместо ее вызова
uint32_t f(size_t idx, vector<int> &v) {
v[idx] = 42;
}
for (size_t i = 0; i < n; ++i) {
f(i, v);
}Инлайнинг - встраивание тела функции, вместо ее вызова
uint32_t f(size_t idx, vector<int> &v) {
v[idx] = 42;
}
for (size_t i = 0; i < n; ++i) {
f(i, v);
}Инлайнинг - встраивание тела функции, вместо ее вызова
uint32_t f(size_t idx, vector<int> &v) {
v[idx] = 42;
}
for (size_t i = 0; i < n; ++i) {
v[i] = 42;
}for (size_t i = 0; i < n; ++i) {
f(i, v);
}Инлайнинг - встраивание тела функции, вместо ее вызова
uint32_t f(size_t idx, vector<int> &v) {
v[idx] = 42;
}
for (size_t i = 0; i < n; ++i) {
v[i] = 42;
}Сейчас решение о необходимости встраивать функции компилятор принимает на основе различных эвристик (бюджет инлайнинга и др.)
Ручные формулы можно заменить на ML алгоритм, который выдедет наилучшую формулу инлайнинга в конкретном коде, а также сможет сам выделять признаки.
Окружение для циклических алгоритмов обучения с подкреплением (GYM)
Решает задачу -
Phase Ordering Problem
Эмбединги кода - IR2Vec
Метрика - не только CodeSize,
но и Performance
Часть проекта ML-LLVM
С помощью RL решает задачу -
Phase Ordering Problem
Часть проекта ML-LLVM
С помощью RL решает задачу -
Распределения циклов
Часть проекта ML-LLVM
С помощью RL решает задачу - Аллокации регистров
38/61
MLGO создали RegAlloc Advisor - специальный советник,
который предсказывает с помощью ML распределение регистров и помогает избрать лучшее распределение с целью увеличения perfomance.
Главное отличие от IITH - это интегрировано в основной LLVM-project!
Стоимость инлайнинга - число, для каждого вхождения функции, определяющее стоимость ее инлайнинга.
Расчет стоимости инлайнинга зависит от некотрых эвристик.
MLGO призван обучить ML модель, чтобы не изобретать формулы стоимостей инлайнинга.
Бюджет инлайнинга - число, определяющее какую сумму можно потратить на инлайнинг.
Профит (CodeSize, Performance)
Профит (CodeSize, Performance)
Дополнительное время компиляции
Профит (CodeSize, Performance)
Дополнительное время компиляции
Сложность использования ~ Готовность к продакшену
Профит (CodeSize, Performance)
Дополнительное время компиляции
Сложность использования ~ Готовность к продакшену
Сложность обучения моделей
Время компиляции при 4 потоках: ~ 1 час
Размер директории ./bin: 3.560 G
clang - 190.6 Mb
Время компиляции при 16 потоках: ~ 1 день
Размер директории ./bin: 3.7772 G
clang - 203.16 Mb
Время компиляции при 16 потоках: ~ 1 день
Размер директории ./bin: 3.7772 G
clang - 203.16 Mb
Фокус не удался(
CompilerGym необходимо настраивать, из коробки не работает
Время компиляции при 16 потоках: ~ 1 день, но на больших файлах - fallback
Размер директории ./bin: 3.536 G
clang - 186.06 Mb
Фокус удался!
Из коробки получили прирост в codesize для O3 ~ 1.004
Время компиляции при 16 потоках: ~ 1 день, но на больших файлах - fallback
Размер директории ./bin: 3.536 G
clang - 186.06 Mb
Время компиляции при 4 потоках: ~ 1 час
Размер директории ./bin: 4.381 G
clang - 219.65 Mb
Время компиляции при 4 потоках: ~ 1 час
Размер директории ./bin: 3.832 G
clang - 196.45 Mb
Время компиляции при 4 потоках: ~ 1 час
Размер директории ./bin: 3.832 G
Фокус удался!
Из коробки получили прирост в codesize для Oz ~ 1.143
Время компиляции при 4 потоках: ~ 1 час
Размер директории ./bin: 3.832 G
clang - 196.45 Mb
| Фреймворк | Профит "из коробки" | Оверхед компиляции | Готовность к продакшену | Сложность обучения |
|---|---|---|---|---|
| CompilerGym | Плохо | х25 | Да, но сильно доделывать | Легко |
| IITH - ML-LLVM | Нормально | х60 | Сложно судить, пока не очень | Нормально |
| MLGO | Отлично | Нет | Да | Нормально |
| Фреймворк | Результат | Профит |
|---|---|---|
| Дефолтный инлайнер | 3.2G (3349132) ./bin/ | Стандартно |
| Инлайнер на новом корпусе | 3.1G (3235508) ./bin/ | Нормально |
| Инлайнер MLGO | 3.1G (3173896) ./bin/ | Нормально |
C++ Russia — конференция по разработке на C++. Она сосредоточена на хардкорных докладах: не на общих рассуждениях, а на технической конкретике.
Литвинов Михаил
@litvinovmitch11
CompilerGym - https://github.com/facebookresearch/CompilerGym
IITH-ML-LLVM - https://github.com/IITH-Compilers/ml-llvm-project
Доклад CPP RUSSIA - https://cppconf.ru/talks/e6c7eab1f2f842fd8cb9d3259916ecae
Доклад СТАЧКА - https://ul25.nastachku.ru/применение-ml-в-компиляторах
Предобученная модель для LLVM-project - https://huggingface.co/litvinovmitch11/mlgo-llvm-project-pretrained
Логи tesorboard -
https://github.com/litvinovmitch11/mlgo-llvm-project-pretrained
Форк MLGO - https://github.com/google/ml-compiler-opt
Форк LLVM-project - https://github.com/google/ml-compiler-opt
Литвинов Михаил
@litvinovmitch11
By Michael Litvinov