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

5.5.6. Предварительное разворачивание циклов для программной конвейерной обработки

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

L1: ld4 r4 = [r5],4 // Такт 0

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

add r7 = r4,r9;; // Такт 2

st4 [r6] = r7,4 // Такт 3

br.cloop L1;; // Такт 3

Конвейерная версия этого цикла должна иметь ИИ не меньше 2, потому, что для трех инструкций памяти имеется только два устройства памяти. Если развернуть цикл вдвое до начала программной конвейерной обработки и принять, что сохранение не зависимо от загрузки, то для нового цикла может быть достигнуто ИИ = 3. Таким образом, ИИ в 1.5 раза эффективнее, чем в первоначальном исходном цикле. Ниже приводится возможный конвейер для развернутого цикла:

стадия 1: (p16) ld4 r4 = [r5],8 // нечетная итерация

(p16) ld4 r9 = [r8],8;; // нечетная итерация

стадия 2: (p16) ld4 r14 = [r15],8 // четная итерация

(p16) ld4 r19 = [r18],8;; // четная итерация

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

стадия 3: (p18) add r7 = r4,r9 // нечетная итерация

(p17) add r17 = r14,r19;; // четная итерация

стадия 4: // --- пустой такт

(p19) st4 [r6] = r7,8 // нечетная итерация

(p18) st4 [r16] = r17,8;; // четная итерация

Развернутый цикл содержит две копии исходного тела цикла, одна соответствует четным исходным итерациям, а вторая – нечетным исходным итерациям. Назначение предикатов стадии должно это учитывать. Вспомним, что каждая единица, записанная в р16, последовательно разрешает все стадии для новой исходной итерации. В течение первой стадии нашего конвейера, предикатом стадии для нечетной итерации являетсяр16. Предикат стадии для четной итерации пока не существует. В течение второй стадии нашего конвейера предикатом стадии для нечетной итерации являетсяр17, а новый предикат стадии для четной итерации находится вр16. Таким образом, в пределах одной и той же стадии конвейера, когда предикат стадии для нечетной операции находится в предикатном регистре Х, тогда предикат стадии для четной операции находится в предикатном регистре Х–1. Ниже показан псевдокод, реализующий этот конвейер, при неизвестном индексе выхода из цикла:

add r15 = r5,4

add r18 = r8,4

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

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

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

L1:

(p16) ld4 r33 = [r5],8 // Такт 0 нечетная итерация

(p18) add r39 = r35,r38 // Такт 0 нечетная итерация

(p17) add r38 = r34,r37 // Такт 0 четная итерация

(p16) ld4 r36 = [r8],8 // Такт 0 нечетная итерация

br.cexit.spnt L3;; // Такт 0

(p16) ld4 r33 = [r15],8 // Такт 1 четная итерация

(p16) ld4 r36 = [r18],8;; // Такт 1 четная итерация

(p19) st4 [r6] = r40,8 // Такт 2 нечетная итерация

(p18) st4 [r16] = r39,8 // Такт 2 четная итерация

L2: br.ctop.sptk L1;; // Такт 2

L3:

Обратите внимание, что стадии не равны по длине. Стадии 1 и 3 по одному такту каждая, а стадии 2 и 4 по два такта каждая. Кроме того, длина фазы эпилога меняется в зависимости от индекса выхода из цикла. Если индекс выхода – нечетный, то количество стадий эпилога – 3, начинается после br.exitи заканчивается наbr.ctop. Если же индекс выхода – четный, то количество стадий эпилога – 2, начинается послеbr.ctopи заканчивается наbr.ctop. ЕС должно быть установлено с учетом максимального количества стадий эпилога. Таким образом, для этого примера ЕС инициализируется к 4. Когда индекс выхода из цикла – четный, то выполняется одна дополнительная стадия эпилога, аbr.exit L3принимается. Все предикаты стадий, используемые в течение дополнительных стадий эпилога, равны 0, так что ничего не выполняется.

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

add r15 = r5,4

add r18 = r8,4

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

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

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

L1:

(p16) ld4 r33 = [r5],8 // Такт 0 нечетная итерация

(p18) add r39 = r35,r38 // Такт 0 нечетная итерация

(p17) add r38 = r34,r37 // Такт 0 четная итерация

(p16) ld4 r36 = [r8],8 // Такт 0 нечетная итерация

br.cexit.spnt L4;; // Такт 0

L4:

(p16) ld4 r33 = [r15],8 // Такт 1 четная итерация

(p16) ld4 r36 = [r18],8;; // Такт 1 четная итерация

(p19) st4 [r6] = r40,8 // Такт 2 нечетная итерация

(p18) st4 [r16] = r39,8 // Такт 2 четная итерация

L2: br.ctop.sptk L1;; // Такт 2

L3:

Если индекс выхода из цикла – четный, то выполняем две стадии эпилога и выходим из ядерного цикла через br.ctop. Если индекс выхода – нечетный, то выполняются две стадии эпилога, а затем принимается переходbr.cexit. Поскольку адресат перехода – это следующая по порядку связка (L4), то третья стадия эпилога будет выполнена прежде, чем мы выйдем из ядерного цикла черезbr.ctop. Эта оптимизация сохраняет одну стадию в конце цикла, когда индекс выхода четный, и выгоден для небольших индексов выхода из цикла.

Хотя развертывание может быть выгодно, но имеется несколько соображений против развертывания программного конвейера. Развертывание уменьшает индекс выхода из цикла, заданный для конвейера, и таким образом может делать конвейерную обработку цикла нежелательной, т.к. низкие значения индекса выхода из цикла иногда выполняются быстрее без конвейерной обработки. Развертывание, также увеличивает размер кода, который может неблагоприятно влиять на эффективность кэша инструкций. Развертывание наиболее выгодно для маленьких циклов, потому что для них, по сравнению с большими циклами, потенциальная деградация эффективности из-за используемых ресурсов – больше, а влияние развертывания на производительность кэша инструкций – меньше.