Branch delay slot¶
Branch delay slot — это инструкция, расположенная в конвейере процессора непосредственно после инструкции перехода (branch), которая выполняется независимо от того, сработает ли переход или нет. Данный механизм является особенностью архитектуры процессоров с фиксированной длиной инструкций, использующих конвейеризацию, и применялся для снижения потерь производительности при выполнении условных и безусловных переходов.
¶Принцип работы
В конвейерных процессорах выполнение инструкции разбивается на несколько этапов (выборка, декодирование, исполнение, запись результата). При встрече инструкции перехода процессор не знает заранее, по какому адресу продолжится выполнение, пока не завершится этап декодирования или исполнения. Это приводит к простою конвейера (пузырю), так как следующая инструкция ещё не выбрана.
Для устранения этого простоя архитекторы ввели понятие branch delay slot. Инструкция, помещённая в этот слот, всегда выбирается и исполняется, даже если переход должен изменить ход выполнения программы. Таким образом, полезная работа выполняется в течение того такта, когда конвейер в противном случае простаивал бы.
¶Архитектуры, использующие branch delay slot
Механизм применялся в ряде известных процессорных архитектур:
- MIPS — одна из самых известных реализаций. В архитектуре MIPS I—III branch delay slot является обязательным, и компилятор всегда заполняет его либо полезной инструкцией, либо инструкцией
nop(no operation). - SPARC — использует ветвление с задержкой, причём в некоторых версиях существует возможность отмены (annulling) инструкции в слоте, если переход не сработал.
- PA-RISC (Hewlett-Packard) — также включает branch delay slot.
- SuperH — японская архитектура, применявшаяся во встраиваемых системах.
В архитектуре x86 branch delay slot не используется, так как она имеет переменную длину инструкций и сложную схему предсказания переходов. В ARM (до версии ARMv8) также отсутствует классический branch delay slot, хотя ранние версии имели некоторые особенности с условным исполнением.
¶Заполнение слота
Компилятор или ассемблер должен решить, чем заполнить branch delay slot. Возможны три стратегии:
- Полезная инструкция — из кода до перехода выбирается инструкция, которая может быть выполнена без побочных эффектов, если переход сработает. Это наиболее эффективный способ, но требует анализа зависимостей.
- Инструкция из целевого адреса — если переход гарантированно сработает (безусловный переход), можно поместить первую инструкцию из целевого блока.
- Инструкция
nop— если ни одна из стратегий не применима, слот заполняется пустой операцией. Это снижает производительность, но сохраняет корректность.
¶Проблемы и ограничения
Branch delay slot создаёт ряд сложностей:
- Усложнение компилятора — требуется анализ потока данных для безопасного перемещения инструкций.
- Сложность отладки — при пошаговом исполнении кода поведение инструкции в слоте может быть неочевидным для программиста.
- Исключения и прерывания — если инструкция в слоте вызывает исключение, поведение процессора должно быть чётко определено спецификацией.
- Ограничение длины конвейера — с увеличением глубины конвейера один слот перестаёт покрывать все потери, и механизм становится неэффективным.
¶Отказ от механизма
Начиная с конца 1990-х годов, развитие технологии предсказания переходов позволило значительно снизить потери от ветвлений без использования branch delay slot. Современные процессоры используют сложные схемы динамического предсказания, спекулятивного исполнения и переименования регистров. В архитектуре MIPS (начиная с MIPS IV) слот стал опциональным, а в более поздних версиях (MIPS32/64) он сохранён для обратной совместимости, но компиляторы часто заполняют его инструкцией nop.
В архитектуре RISC-V, созданной в 2010-х годах, branch delay slot был полностью исключён из спецификации как устаревший механизм, усложняющий разработку компиляторов и отладку.
¶Значение для программирования
Для программиста на ассемблере знание branch delay slot критически важно: размещение инструкции в слоте требует понимания порядка исполнения. Например, в MIPS инструкция, следующая за beq (branch if equal), выполняется всегда, даже если переход не происходит. Это часто приводит к ошибкам у начинающих разработчиков.
В языках высокого уровня механизм прозрачен для программиста, так как компилятор автоматически управляет заполнением слотов. Однако в системном программировании и при написании критичного по времени кода (например, обработчиков прерываний) учёт особенностей архитектуры обязателен.