- •5. Программная конвейерная обработка и поддержка циклов
- •5.1. Краткий обзор
- •5.2. Терминология циклов и основная поддержка циклов
- •5.3. Оптимизация циклов
- •5.3.1. Развертывание цикла
- •5.3.2. Программная конвейерная обработка
- •5.4. Особенности поддержки циклов в архитектуре Intel Itanium
- •5.4.1. Ротация регистров
- •5.4.3. Переходы циклов с программной конвейерной обработкой
- •5.4.3.1. Переходы счетных циклов
- •5.4.2. Замечание об инициализации ротации предикатов
- •5.4.3.2. Пример счетного цикла
- •5.4.3.3. Переходы while-циклов
- •5.4.4. Краткий обзор терминологии
- •5.5. Оптимизация циклов в архитектуре Intel Itanium
- •5.5.1. While-циклы
- •5.5.2. Циклы с инструкциями, имеющими предикаты
- •5.5.3. Циклы с множеством выходов
- •5.5.3.1. Преобразование циклов с несколькими выходами в циклы с одним выходом.
- •5.5.3.2. Конвейерная обработка с явными многократными выходами
- •5.5.4. Соображения о программной конвейерной обработке
- •5.5.5. Программная конвейерная обработка и предварительные загрузки
- •5.5.5.1. Ограничения емкости
- •5.5.5.2. Конфликты в alat
- •5.5.6. Предварительное разворачивание циклов для программной конвейерной обработки
- •5.5.7. Осуществление сокращений
- •5.5.8. Явный пролог и эпилог
- •5.5.9. Устранение избыточной загрузки в циклах
- •5.6. Итоги
5.5.3.2. Конвейерная обработка с явными многократными выходами
Второй подход заключается в объединении трех последних инструкций в цикле в инструкцию br.cloop, а затем цикл конвейеризируется. Конвейер, использующий этот подход, показан ниже:
стадия 1: ld4.s r4 = [r5],4;; // ИИ = 1
стадия 4: ld4.s r9 = [r4];;
стадия 6: cmp.eq.unc p1,p0 = r9,r7
(p1) br.cond exit
br.cloop L1;;
В этом конвейере имеются пять спекулятивных стадий, потому что не спекулятивное решение инициализировать другую итерацию цикла, не может быть сделано до выполнения br.cond и br.cloop в стадии 6. Ниже показан код, осуществляющий этот конвейер в предположении, что индекс выхода равен 200.
mov lc = 204
mov ec = 1
mov pr.rot = 1 << 16;; // PR16 = 1, остальные = 0
L1:
ld4.s r32 = [r5],4 // Такт 0
(p21) chk.s r38, recovery // Такт 0
(p21) cmp.eq.unc p1,p0 = r38,r7 // Такт 0
ld4.s r36 = [r35] // Такт 0
(p1) br.cond exit // Такт 0
L2: br.ctop.sptk L1; // Такт 0
Когда, либо br.cond либо br.ctop выходит из ядерного цикла – завершается последняя исходная итерация. Таким образом, ЕС инициализировано в 1 и нет явного блока эпилога, сгенерированного для выхода. Регистр LC инициализирован к пяти (после 199 шагов) потому, что имеются пять спекулятивных стадий. Цель первых пяти выполнений br.ctop – это просто сохранить движение цикла до тех пор, пока не будет сгенерирован первый допустимый предикат для br.cond. При каждом таком выполнении LC декрементируется, поэтому, для компенсации, к числу инициализации LC должна быть добавлена пятерка.
При втором подходе достигается меньший ИИ. Этот конвейерный код будет также работать, если LC инициализирован к 199, а ЕС инициализирован к 6. Однако если будет принят преждевременный выход, то LC будет декрементирован слишком много раз, и будет нуждаться в корректировке, если он используется в адресате преждевременного выхода. Если есть какой-нибудь эпилог при принятии раннего выхода, то этот эпилог должен быть явным.
5.5.4. Соображения о программной конвейерной обработке
Могут быть случаи, когда не желательно конвейеризировать цикл. Программная конвейерная обработка увеличивает производительность итераций, но может увеличить время, требуемое для завершения отдельной итерации. В результате, циклы с очень маленькими индексами завершения могут получить меньшую эффективность при конвейерной обработке. Например, рассмотрим следующий цикл:
L1: ld4 r4 = [r5],4 // Такт 0
ld4 r7 = [r8],4;; // Такт 0
st4 [r6] = r4,4 // Такт 2
st4 [r9] = r7,4 // Такт 2
br.cloop L1;; // Такт 2
Далее приведен возможный конвейер при ИИ = 2:
stage 1: ld4 r4 = [r5],4 // Такт 0
ld4 r7 = [r8],4;; // Такт 0
--- // пустой такт
stage 2: --- // пустой такт
st4 [r6] = r4,4 // Такт 3
st4 [r9] = r7,4;; // Такт 3
В исходном цикле, одна итерация заканчивается каждые три такта. В цикле с программной конвейерной обработкой требуется четыре такта для завершения первой итерации. После этого итерации завершаются каждые два цикла. Если индекс выхода – два, то время выполнения обеих версий цикла одно и то же – 6 тактов. Если средний индекс выхода из цикла меньше двух, то версия цикла с программной конвейерной обработкой медленнее исходного цикла.
Кроме того, не желательно организовывать конвейер для цикла с плавающей точкой, который содержит вызов функции. Число регистров с плавающей точкой используемых циклом не известно до тех пор, пока цикл не будет превращен в конвейер. В полученном конвейере может быть трудно, найти пустые слоты для инструкций, необходимых для сохранения и восстановления регистров с плавающей точкой на время вызова функции.
