CISC и RISC: набор команд

Предпосылки: процессор (конвейер, fetch, decode, суперскалярность, внеочерёдное исполнение, спекулятивное исполнение).

Процессор | ABI и размещение данных

Когда компилятор вычисляет a += b, ему нужно понять, какими командами говорить процессору. Но разные производители разговаривают на разных языках инструкций: процессор Intel не поймёт программу, скомпилированную для ARM, и наоборот. Какой набор команд понимает процессор? Этот вопрос определяет, как компилятор превращает исходный код в выполняемые команды.

Компилятор генерирует код для одной и той же операции — a += b, когда обе переменные лежат в памяти. На x86 получаются две инструкции, на ARM — четыре. Разница не в том, как процессор устроен внутри — конвейер, суперскалярность, внеочерёдное исполнение одинаковы по сути. Разница в том, какие инструкции процессор обещает понимать. Этот контракт между процессором и программным обеспечением — ISA (Instruction Set Architecture — архитектура набора команд): какие инструкции доступны, какие регистры видны программисту, как адресуется память. Две архитектуры (x86 и ARM) решают эту задачу принципиально по-разному.

Два подхода к набору команд

CISC (Complex Instruction Set Computer — «компьютер со сложным набором команд») — подход x86, доминирующего на серверах и десктопах. Одна инструкция может сделать много: загрузить значение из памяти, выполнить арифметику и записать результат обратно. Для a += b с обеими переменными в памяти достаточно двух инструкций: загрузить b в регистр и выполнить сложение с записью в память за одну операцию. Инструкции переменной длины — от 1 до 15 байт.

RISC (Reduced Instruction Set Computer — «компьютер с сокращённым набором команд») — подход ARM, доминирующего в мобильных и встроенных устройствах. Каждая инструкция делает ровно одну вещь. Фиксированная длина 4 байта в AArch64 (64-битное состояние исполнения ARMv8+). Для того же a += b из памяти нужно четыре инструкции: загрузить a, загрузить b, сложить, записать обратно.

Четыре инструкции вместо двух — значит ARM медленнее? Нет. Ключ — в том, что происходит внутри процессора на стадии decode.

CISC снаружи, RISC внутри

Современные x86-процессоры внутри работают как RISC: сложные CISC-инструкции на стадии decode разбиваются на микрооперации (micro-ops, μops) — простые внутренние команды фиксированной длины. Конвейер, суперскалярный движок и блок внеочерёдного исполнения работают уже с μops. Та самая операция a += b, записанная двумя x86-инструкциями, внутри процессора превращается в те же загрузку, сложение и запись — но с накладными расходами на трансляцию. Снаружи — CISC ради совместимости с 45+ годами экосистемы. Внутри — RISC ради эффективности конвейера.

Конвейер делится на фронтенд (front-end — «передняя часть»: стадии fetch и decode, которые готовят инструкции к исполнению) и бэкенд (back-end — «задняя часть»: исполнительные блоки, диспетчеризация и фиксация результатов). Оба пути — x86 и ARM — приходят к похожему бэкенду. Главная асимметрия — во фронтенде: x86-декодер дополнительно разбивает внешние CISC-инструкции на μops, что требует больше транзисторов и энергии.

flowchart LR
    X["x86 instruction stream<br>переменная длина"] --> XD["Сложный decode<br>(фронтенд)"]
    XD --> U["μops"]
    U --> XB["Бэкенд"]

    A["ARM instruction stream<br>фиксированные 4 байта"] --> AD["Простой decode<br>(фронтенд)"]
    AD --> AB["Бэкенд"]

Цена сложного фронтенда — энергия. Более простой декодер ARM — одна из причин, почему ARM-процессоры расходуют меньше энергии на инструкцию. На практике это проявляется в мобильных устройствах (где ARM доминирует) и постепенно — на серверах: AWS (Amazon Web Services) заявляет до 60% лучшую энергоэффективность у Graviton3 по сравнению с сопоставимыми x86-инстансами.

Выбор ISA не определяет производительность напрямую — обе архитектуры используют одни и те же техники в бэкенде: конвейер, суперскалярность, внеочерёдное исполнение, спекулятивное исполнение. Разница — в балансе энергия/производительность и в экосистеме.

Выбор ISA определяет первичный компромисс: сложный декодер (CISC, энергозатратный, совместимый) vs простой (RISC, экономный, экосистемозависимый). Но это не единственное соглашение между процессором и программой. ISA говорит “вот эти команды я понимаю”. Программе нужно знать и другое: куда кладутся аргументы функций, где лежит результат, как выравниваются данные в памяти. Эти правила определяют ABI — бинарный интерфейс приложений, который связывает компилятор, процессор и операционную систему в единый контракт.

Sources


Процессор | ABI и размещение данных