Месяц назад я попытался сосчитать, сколько разных инструкций поддерживается современными процессорами, и насчитал 945 в Ice Lake. Комментаторы затронули интересный вопрос: какая часть всего этого разнообразия реально используется компиляторами? Например, некто Pepijn de Vos в 2016 подсчитал, сколько разных инструкций задействовано в бинарниках у него в /usr/bin, и насчитал 411 — т.е. примерно треть всех инструкций x86_64, существовавших на тот момент, не использовались ни в одной из стандартных программ в его ОС. Другая любопытная его находка — что код для x86_64 на треть состоит из инструкций
Я решил развить исследование de Vos, взяв в качестве «эталонного кода» компилятор LLVM/Clang. У него сразу несколько преимуществ перед содержимым /usr/bin неназванной версии неназванной ОС:
Начну со статистики по мартовскому релизу LLVM 10.0:
В прошлом топике комментаторы упомянули, что самый компактный код у них получается для SPARC. Здесь же видим, что бинарник для AArch64 оказывается на треть меньше что по размеру, что по общему числу инструкций.
А вот распределение по числу инструкций:
Неожиданно, что код для SPARC на 11% состоит из
А как набор используемых инструкций менялся со временем?
Единственная ISA, для которой в каждом релизе есть официальный бинарник — это i386:
Серая линия, отложенная на правой оси — это число разных инструкций, использованных в компиляторе. Как мы видим, некоторое время назад компилятор компилировался гораздо разнообразнее.
Самые первые версии LLVM, до появления clang, выпускались ещё и с бинарниками для SPARC. Потом поддержка SPARC утратила актуальность, и вновь она появилась лишь через 14 лет — с на порядок увеличившимся числом
Исторически следующая ISA, для которой выпускались бинарники LLVM — это PowerPC: сначала для Mac OS X и затем, после десятилетнего перерыва, для RHEL. Как видно из графика, переход после этого перерыва к 64-битному варианту ISA сопровождался заменой большинства
В бинарниках для x86_64 и ARM частота использования разных инструкций почти не изменялась:
При подсчёте инструкций ARM я отсекал суффиксы условий — вместе с ними получалось около тысячи разных инструкций, но даже и без них ARM сильно опережает другие ISA по разнообразию генерируемых инструкций. Таким образом, слой
Наконец, самая недавняя ISA, для которой публикуются официальные бинарники — это AArch64. Здесь интересно то, что
PowerPC и AArch64 оказались единственными ISA, для которых число разных используемых инструкций всё растёт и растёт.
mov
. (В общем-то известно, что одних инструкций mov
достаточно, чтобы написать любую программу.)Я решил развить исследование de Vos, взяв в качестве «эталонного кода» компилятор LLVM/Clang. У него сразу несколько преимуществ перед содержимым /usr/bin неназванной версии неназванной ОС:
- С ним удобно работать: это один огромный бинарник, по размеру сопоставимый со всем содержимым /usr/bin среднестатистического линукса;
- Он позволяет сравнить разные ISA: на releases.llvm.org/download.html доступны официальные бинарники для x86, ARM, SPARC, MIPS и PowerPC;
- Он позволяет отследить исторические тренды: официальные бинарники доступны для всех релизов начиная с 2003;
- Наконец, в исследовании компиляторов логично использовать компилятор и в качестве подопытного объекта :-)
Начну со статистики по мартовскому релизу LLVM 10.0:
ISA | Размер бинарника | Размер секции .text | Общее число инструкций | Число разных инструкций |
---|---|---|---|---|
AArch64 | 97 МБ | 74 МБ | 13,814,975 | 195 |
ARMv7A | 101 МБ | 80 МБ | 15,621,010 | 308 |
i386 | 106 МБ | 88 МБ | 20,138,657 | 122 |
PowerPC64LE | 108 МБ | 89 МБ | 17,208,502 | 288 |
SPARCv9 | 129 МБ | 105 МБ | 19,993,362 | 122 |
x86_64 | 107 МБ | 87 МБ | 15,281,299 | 203 |
А вот распределение по числу инструкций:
Неожиданно, что код для SPARC на 11% состоит из
nop
-ов, заполняющих branch delay slots. Для i386 среди самых частых инструкций видим и int3
, заполняющую промежутки между функциями, и nop
, используемую для выравнивания циклов на строки кэша. Наблюдение de Vos о том, что код на треть состоит из mov
, подтверждается на обоих вариантах x86; но даже и на load-store-архитектурах mov
оказывается если не самой частой инструкцией, то второй после load.А как набор используемых инструкций менялся со временем?
Единственная ISA, для которой в каждом релизе есть официальный бинарник — это i386:
Серая линия, отложенная на правой оси — это число разных инструкций, использованных в компиляторе. Как мы видим, некоторое время назад компилятор компилировался гораздо разнообразнее.
int3
стала использоваться для заполнения промежутков только с 2018; до этого использовались такие же nop
, как и для выравнивания внутри функций. Здесь же видно, что выравнивание внутри функций стало использоваться с 2013; до этого nop
-ов было гораздо меньше. Ещё интересно, что до 2016 mov
-ы составляли почти половину компилятора.Самые первые версии LLVM, до появления clang, выпускались ещё и с бинарниками для SPARC. Потом поддержка SPARC утратила актуальность, и вновь она появилась лишь через 14 лет — с на порядок увеличившимся числом
nop
-ов:Исторически следующая ISA, для которой выпускались бинарники LLVM — это PowerPC: сначала для Mac OS X и затем, после десятилетнего перерыва, для RHEL. Как видно из графика, переход после этого перерыва к 64-битному варианту ISA сопровождался заменой большинства
lwz
на ld
, и вдобавок удвоением разнообразия инструкций:В бинарниках для x86_64 и ARM частота использования разных инструкций почти не изменялась:
При подсчёте инструкций ARM я отсекал суффиксы условий — вместе с ними получалось около тысячи разных инструкций, но даже и без них ARM сильно опережает другие ISA по разнообразию генерируемых инструкций. Таким образом, слой
b
на этом графике включает и все условные переходы тоже. Для остальных ISA, где условными могут быть только переходы и немногие другие инструкции, суффиксы условий при подсчёте не отсекались.Наконец, самая недавняя ISA, для которой публикуются официальные бинарники — это AArch64. Здесь интересно то, что
orr
с прошлого года почти перестала использоваться:PowerPC и AArch64 оказались единственными ISA, для которых число разных используемых инструкций всё растёт и растёт.