Скачиваний:
58
Добавлен:
16.04.2013
Размер:
260.1 Кб
Скачать

5.5.7. Осуществление сокращений

В следующем примере суммируются произведения, накапливаемые в регистре r7:

mov f7 = 0;; // инициализация sum

L1: ldfs f4 = [r5],4

ldfs f9 = [r8],4;;

fma f7 = f4,f9,f7;; // накопление

br.cloop L1 ;;

Эффективность ограничена временем ожидания инструкции fma, которую, для этих примеров мы принимаем равной 5 тактам. Конвейерная версия этого цикла должна иметь ИИ не меньше пяти, потому что время ожиданияfmaэто пять. С помощью использования ротации регистров, цикл может быть преобразован к нижеприведенному виду.

Обратите внимание, что цикл еще не конвейеризирован. Ротация и регистров и специальные циклические переходы используются для разрешения оптимизации до программной конвейерной обработки.

mov lc = 199 // LC = счетчик цикла - 1

mov ec = 1 // Нет конвейера, т.о. нет эпилога

mov f33 = 0 // инициализируем 5 сумм

mov f34 = 0

mov f35 = 0

mov f36 = 0

mov f37 = 0;;

L1: ldfs f4 = [r5],4

ldfs f9 = [r8],4;;

fma f32 = f4,f9,f37;; // накопление

br.ctop L1 ;;

fadd f10 = f33,f34 // сложение сумм

fadd f11 = f35,f36;;

fadd f12 = f10,f11;;

fadd f7 = f12,f37

Этот цикл обслуживает пять независимых сумм в регистрах f33-f37. Инструкцияfmaв итерации Х производит результат, который используется инструкциейfmaв итерации Х+5. Итерации от Х до Х+4 независимы, позволяя ИИ достигнуть единицы. Ниже показан код для конвейерной версии цикла, при условии двух портов памяти и девяти тактов времени ожидания загрузки с плавающей точкой:

mov lc = 199 // LC = счетчик цикла - 1

mov ec = 10 // EC = стадии эпилога + 1

mov pr.rot=1<<16 // PR16 = 1, остальные = 0

mov f33 = 0 // инициализация сумм

mov f34 = 0

mov f35 = 0

mov f36 = 0

mov f37 = 0

L1:

(p16)ldfs f50 = [r5],4 // Такт 0

(p16)ldfs f60 = [r8],4 // Такт 0

(p25)fma f41 = f59,f69,f46 // Такт 0

br.ctop.sptk L1;; // Такт 0

fadd f10 = f42,f43 // сложение сумм

fadd f11 = f44,f45 ;;

fadd f12 = f10,f11 ;;

fadd f7 = f12,f46

5.5.8. Явный пролог и эпилог

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

ld4 r3 = [r5] ;;

L1: ld4 r6 = [r8],4 // Такт 0

ld4 r5 = [r9],4 ;; // Такт 0

add r7 = r3,r6 ;; // Такт 2

ld4 r3 = [r5] // Такт 3

and r10 = 3,r7;; // Такт 3

cmp.ne p1,p0=r10,r11 // Такт 4

(p1) br.cond L1 ;; // Такт 4

Далее приводится возможный конвейер для цикла:

stage 1: ld4.s r6 = [r8],4 // ИИ = 2

ld4.s r5 = [r9],4 ;;

--- // пустой такт

stage 2: --- // пустой такт

ld4.s r36 = [r5]

add r7 = r37,r6 ;;

стадия 3: (p18)and r10 = 3,r7 ;;

(p18)cmp.ne p1,p0 = r10,r11

(p1) br.wtop L1 ;;

Обратите внимание, что в этом коде инструкции ld4иaddв стадии 2, были переупорядочены. Для устранения регистровой зависимостиWARмеждуaddи ld4использовалась ротация регистров. Первые две стадии являются спекулятивными. Ниже показан код реализующий конвейер:

ld4 r36 = [r5]

mov ec = 2

mov pr.rot = 1 << 16 ;; // PR16 = 1, остальные = 0

L1: ld4.s r32 = [r8],4 // Такт 0

ld4.s r34 = [r9],4 // Такт 0

(p18) and r40 = 3,r39 ;; // Такт 0

ld4.s r36 = [r35] // Такт 1

add r38 = r37,r33 // Такт 1

(p18) chk.s r40, recovery // Такт 1

(p18) cmp.ne p17,p0 = r40,r11 // Такт 1

(p17) br.wtop L1 ;; // Такт 1

Проблема с эти конвейерным циклом состоит в том, что значение, записанное в r36до цикла, перезаписывается прежде, чем оно будет использовано сложением. Значение перезаписывается загрузкой в первой ядерной итерации. Эта загрузка находится во второй стадии конвейера, но не может управляться в течение первой ядерной итерации, поскольку она спекулятивна и не имеет предиката стадии. Эта проблема может быть решена путем срезания (peelingoff) одной итерации ядра и исключения из этой копии любых инструкций, которые не находятся в первой стадии конвейера, как это показано ниже.

Обратите внимание, что номера адресуемых регистров для инструкций в явном прологе, были увеличены на один. Это объясняется тем фактом, что нет ротации в конце срезанной ядерной итерации.

ld4 r37 = [r5]

mov ec = 1

mov pr.rot = 1<<17;; // PR17 = 1, остальные = 0

ld4 r33 = [r8],4

ld4 r35 = [r9],4

L1: ld4.s r32 = [r8],4 // Такт 0

ld4.s r34 = [r9],4 // Такт 0

(p18)and r40 = 3,r39 ;; // Такт 0

ld4.s r36 = [r35] // Такт 1

add r38 = r37,r33 // Такт 1

(p18)chk.sr40, recovery // Такт 1

(p18)cmp.ne p17,p0 = r40,r11 // Такт 1

(p17)br.wtop L1 ;; // Такт 1

В некоторых случаях может быть достигнута более высокая эффективность, путем генерации отдельных блоков для всей или для части фазы пролога и/или эпилога. Это свободно от трассы выполнения исходногоконвейерного счетного цикла (пример в начале раздела 5.3), функциональные устройства которого, используются в течение фаз пролога и эпилога. Часть пролога и эпилога могла быть срезана и объединена с кодом предшествующим циклу, либо следующим за циклом. Далее представлена конвейерная версия этого счетного цикла с явным прологом и эпилогом:

mov lc = 196

mov ec = 1

prolog:

ld4 r35 = [r5],4;; // Такт 0

ld4 r34 = [r5],4 ;; // Такт 1

ld4 r33 = [r5],4 // Такт 2

add r36 = r35,r9 ;; // Такт 2

L1:

ld4 r32 = [r5],4

add r35 = r34,r9

st4 [r6] = r36,4

L2: br.ctop L1 ;;

epilog:

add r35 = r34,r9 // Такт 0

st4 [r6] = r36,4 ;; // Такт 0

add r34 = r33,r9 // Такт 1

st4 [r6] = r35,4 ;; // Такт 1

st4 [r6] = r34,4 // Такт 2

Полные, пролог (первые три инструкции ядерного цикла) и эпилог (последние три инструкции) были срезаны. Не делалось никакой попытки перепланирования срезанных инструкций. Предикаты стадий были удалены из инструкций, поскольку они не требуются для управления фазами пролога и эпилога. Их удаление из пролога, делает инструкции пролога независимыми от ротируемых предикатов и устраняет потребность в переходах программной конвейерной обработки циклов между стадиями пролога. Таким образом, полный пролог не зависит от инициализаций LC и ЕС, которые предшествуют ему. Номера регистров в прологе и эпилоге были откорректированы для учета недостатка ротации между стадиями в течение упомянутых фаз.

Примечание. Этот код предполагает, что индекс выхода из цикла не меньше четырех. Если минимальный индекс выхода не известен во время компиляции, то перед прологом должна быть добавлена проверка индекса выхода во время проверки. Если индекс выхода меньше четырех, то управление передается на копию первоначального цикла.

Если этот конвейерный цикл вложен внутрь внешнего цикла, то здесь существует возможность дальнейшей оптимизации. Внешний цикл, можно повернуть так, что ядерный цикл окажется вверху, далее следует эпилог для текущей итерации внешнего цикла и пролог для следующей итерации внешнего цикла. Кроме того, перед внешним циклом добавляется копия пролога.

Примечание. Для более ранней трассы выполнения счетного цикла, функциональные устройства, использующие пролог и эпилог, являются дополнительными, так что они могут очень хорошо перекрываться друг с другом.

Недостатком создания явного пролога или эпилога, является расширение кода.