How is Ultrassembler so fast?
Ultrassembler — библиотека RISC-V-ассемблера, встроенная в проект Chata.
В отличие от as
и llvm-mc
, она вызывается прямо из C++, без system()
и временных файлов, что критично для встроенных систем.
Скорость
Тест на 16 тыс. инструкций:
- Ultrassembler ≈ 10× быстрее
as
, 20× быстрееllvm-mc
. - 1 RISC-V инструкция ≈ 1000 x86-инструкций (у конкурентов 10–20 тыс.).
Код на чистом C++; можно добавить ассемблерные вставки.
Ключевые оптимизации
Исключения
GCC-реализация «zero-overhead»: штрафа нет, пока исключений нет.
Ошибки встречаются редко и видны человеку, поэтому даже 1 с на обработку незаметна.
std::expected
дал −10 %, так как нормальный путь стал дороже.
Быстрые структуры
2000+ RISC-V-инструкций требуют мгновенного поиска.
Вместо std::unordered_map
используется perfect-hash таблица от gperf
, генерирующая O(1) без коллизий.
Размер таблицы компактен, кэш-эффективен.
Парсинг
- Регистры идентифицируются по первым 2–3 символам через
switch
. - Нет
std::string
, толькоstd::string_view
и статические буферы. - Лексемы разбираются за один проход без регулярных выражений.
Кодогенерация
- Шаблоны на этапе компиляции формируют битовые маски инструкций.
- Варианты одной инструкции разворачиваются в
constexpr
-таблицы, что убирает ветвления в рантайме.
Память
- Все выделения через стековые
std::array
/std::string_view
. - Нет
new
/malloc
, следовательно, нет аллокационных штрафов и кэш-промахов.
Платформенные трюки
[[likely]]
/[[unlikely]]
для подсказок ветвления.__builtin_expect
там, где компилятор не догадывается.- LTO + PGO дают ещё 5–7 %.
Итог
Ultrassembler показывает, что «низкоуровневый» C++ без искусственных ограничений может обгонять даже оптимизированные GNU-утилиты.
Комментарии (34)
- В обсуждении разобрали миф о «системном вызове при каждом росте контейнера» — реальные аллокаторы переиспользуют память и делают syscall лишь при нехватке.
- Участники напомнили, что исключения в C++ не «zero-overhead»; есть компромисс между временем и памятью, и g++ выбирает экономию места.
- Автор статьи подтвердил: пробовал хеширование, но дерево разбора оказалось быстрее; flex/bison тут не при чём, скорее gperf.
- Некоторые посоветовали LLVM C++ API, memory-mapped I/O и std::pmr для ускорения и упрощения кода.
- Большинство сходится: современные ассемблеры и так быстрые, задача скорее академическая, но как «посмотреть, насколько можно ускорить» — интересна.
10-20x Faster LLVM -O0 Back-End (2020)
TPDE-LLVM — новый бэкенд LLVM-O0, который в 10–20 раз быстрее стандартного, при сопоставимой скорости выполнения и росте кода на 10–30 %. Работает с IR Clang-O0/O1, цели x86-64 и AArch64.
Данные SPEC CPU 2017 (x86-64, ускорение компиляции и размер кода относительно LLVM 19 -O0):
бенчмарк | O0 IR | O1 IR |
---|---|---|
perl | 11.4× / 1.27× | 15.1× / 0.97× |
gcc | 12.5× / 1.32× | 17.6× / 1.01× |
omnetpp | 21.5× / 1.24× | 26.5× / 1.03× |
геом.ср. | 13.3× / 1.27× | 17.6× / 0.97× |
Как работает: три прохода — очистка IR, анализ (циклы + liveness), единый codegen (lowering, регистры, кодирование).
Поддержка: как библиотека, llc
-подобный инструмент, патч для Clang. DWARF и улучшенный рег-аллокатор в планах.
Ограничения: не все IR-конструкции, векторы, TLS-глобалы, i260
и т.д.
Что ускорило бы LLVM ещё сильнее:
- убрать
ConstantExpr
внутри функций; - запретить гигантские структуры/массивы как значения;
- упростить доступ к TLS и произвольную битовую арифметику.
Комментарии (6)
- Пользователи обсудили, что Gentoo всё ещё используют с флагом -O8 для максимальной производительности.
- Кто-то спросил, нужно ли добавить пометку «(2020)» к цитируемому тексту.
- Упомянули, что «все» якобы перешли на Arch, где компилятор якобы умеет -O11.
- Уточнили: пост 2025 года, но он цитирует запись 2020-го; попросили модератора исправить.