Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Оптимизирующие преобразования программ. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
849 Кб
Скачать

Пример 50. Фрагмент программы2.4. Преобразования циклов

IF (X > 0); {

A = C+D+E; }

НЕ равносиленB = 2*(C+D);следующему:

IF (X > 0); {

T = C+D; }

A = T+E;

КонецB = 2*T;примера.

2.4. Преобразования циклов

Раскрутка цикла заменяет2.4.1. Раскруткацикл цикла

FOR (I=L; I<R; I++); {

на следующийТЕЛОЦИКЛАфрагмент(I); } программы:

ТЕЛОЦИКЛА(L); ТЕЛОЦИКЛА(L+1);

........................

ТЕЛОЦИКЛА(R);

ГраницыI = R; цикла должны быть константами, известными на этапе компиляции.

Непосредственно раскрутку можно применять лишь для циклов, в теле которых не содержатся помеченные операторы, вызовы функций, операторы goto, continue, break. Эти ограничения можно ослабить. Если есть операторы с меткой, то их следует менять на новые метки с соответствующими изменениями в метках goto. Операторы break следует менять на goto с новой меткой (нового пусто-

51

го) оператора, на который2. Преобпередаётсяазов ния программуправление после выполнения цикла. Если очень нужно делать раскрутку цикла, содержащего continue, то следует в начале каждой копии тела цикла ставить новую метку и оператор continue в каждой копии тела цикла заменять оператором goto на метку следующей копии тела цикла, а оператор continue в последней копии тела цикла удалить (заменить пустым).

После раскрутки счётчику цикла следует присвоить значение последней итерации.

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

Поскольку при раскрутке цикла не меняется порядок выполнения операций, это преобразование всегда эквивалентно. Более того, оператор цикла по своей семантике определяется как краткая запись раскрутки.

Пример 51. Цикл

FOR (I=1; I<=3; I++); {

X[I] = A[I+1];

эквивалентенK[I] = X[Iследующему-1]; } фрагменту программы:

X[1] = A[2];

K[1] = X[0];

X[2] = A[3];

K[2] = X[1];

X[3] = A[4];

КонецK[3] примера= X[2];.

Особенность коротких циклов после преобразования Пример 52. Исходный циклв ассемблерный: код

For (j=1; j<=m; j++) LoopBody(j);

52

После преобразования2.4. Преобразованияв ассемблерныйциклов код принимает примерно (ассемблеры и компиляторы бывают разные) следующий вид:

j =1;

99 j = j+1; If j<m {

LoopBody(j);

На каждой итерации возникают проверки If, присваивания

Goto 99;

}

j = j+1 и передачи управления goto. Если тело цикла невелико, то

время операций, дополнительных к выполнению тела цикла, су-

щественно.

Ус орение кода раскруткой циклов

Исходный цикл:

For (j=1;j<=m; j++)

РаскруткаLoopBody(j);цикла (full loop unrolling):

LoopBody(1);

LoopBody(2);

LoopBody(3);

LoopBody(m); j = m;

ОптимизирующиеАвтоматизациякомпиляторыраскрутки циклане всегдакомпиделаютяторомраскрутку цикла. Двойные треугольные циклы без специального указания, скорее всего, не раскручивают. Эксперименты с GCC показали отсутствие автоматической раскрутки (вручную сделанная раскрутка ускоряет код в 2 раза).

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

53

2. Преобразования программ

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

Двойной треугольныйРаскрутка треугольныхцикл – это такоегнёздгнездоцикловиз двух циклов, в котором у внутреннего цикла одна из границ зависит от счётчика внешнего цикла. Граница внутреннего цикла не является константной на этапе компиляции, но можно делать раскрутку таких циклов вместе (после) с раскруткой вышестоящего цикла.

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

Аналогично можно делать раскрутки гнёзд циклов, в границах циклов которых есть только константы и счётчики вышестоящих циклов.

Например, при преобразовании тайлинг для гнёзд циклов в численных методах решения краевых задач в трёхмерных областях могут возникать тройные «пирамидальные» циклы. Раскрутку таких гнёзд циклов можно делать начиная с внешнего (объемлющего).

При решателе пакета прикладных программ может быть конвертор (предварительный компилятор – предкомпилятор), который выполняет оптимизации, специфичные для данного решателя, в том числе раскрутки гнёзд коротких циклов.

54

2.4. Преобразования циклов

2.4.2. Канонизация циклов

Данное преобразование заменяет всякий цикл равносильным, с левой границей 1 и шагом тоже 1. Иногда используется термин

«нормализация» (do-loop normalization) [2, с. 37]. Это преобразова-

ние применимо к любому оператору цикла

FOR (I=L; I<=N; I = I + h); {

и заменяетТЕЛОЦИКЛАего оператором(I); } цикла

FOR (J=1; J<=(N-L+1)//h; J++); {

ЗдесьТЕЛОЦИКЛА// означает(h*(J-операцию1)+L); }целочисленного деления. К описанным требованиям канонического вида цикла можно добавить ещё одно: последний оператор тела цикла должен быть пустым.

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

1. Сгенерировать новую переменную (обозначим её J) для имени нового счётчика цикла.

2. Всюду в теле цикла заменить старый счётчик цикла I на выражение (h*(J-1)+L). Для выполнения этого преобразования можно использовать подстановку вперёд.

3. В заголовке цикла левая граница заменяется числом 1, а

правая – (N-L+1)//h.

4. Если последний оператор тела исходного цикла не пустой, то надо этот оператор приписать в конце цикла со сгенерированной новой меткой и эту же метку указать в заголовке цикла.

Пункт 2 приведённого алгоритма может заметно увеличитьЗамечаниевремя. выполнения цикла, поскольку заменяет переменную I выражением с тремя операциями. Поэтому вместо пункта 2 иногда при канонизации удобнее использовать пункт 2’:

2’. В теле цикла первым оператором поставить присваивание УI такой= (h*(Jформы-1)+L)канонизации. общий объём вычислений увели-

чится не сильно, но появится дополнительный оператор, и в индексных выражениях вместо нового счётчика цикла J будет стоять индуктивная переменная I.

55

FOR

 

Для цикла

2. Преобразования программ

 

 

 

 

 

 

Пример 53.

 

 

 

(I=K-1; I<=N; I = I + 2); {

IF (A[I]<X);{

 

goto

99;}

 

K = B+X;

канонический99 X = I+X;вид будет следующий:

FOR (J=1; J<=(N-K)//2; I++); { IF (A[2*(J-1)+K-1]<X);{

goto 99;}

K = B+X;

Конец99 X примера= 2*(J-. 1)+K-1+X; }

Канонический вид цикла допускает вариации, в зависимости от дальнейших преобразований. Следует отметить, что для некоторых преобразований удобно иметь цикл, у которого начальное значение счётчика (левая граница) равно 0. Иногда перед циклом и после цикла добавляются пустые операторы, которые перехватывают передачи управления, ведущие на заголовок цикла и выходящие из цикла соответственно [26].

Это преобразование2.4.3. Перестановказаменяет циклблоков в цикле

FOR (I=1; I<=N; I++); {

B1(I);

новымB2(I);циклом}

FOR (I=1; I<=N; I++); {

B2(I);

ДляB1(I);перестановки} блоков в цикле достаточно воспользоваться описанным ранее условием перестановки блоков. Но это условие можно ослабить.

56

Если между2.4. Преоблокамираз вания ц кловB2(I) и B1(I) нет дуг графа ГИСТеоремациклически13. независимой зависимости, то перестановка блоков эквивалентна.

ДляB1(1);доказательстваB2(1); B1(2);достаточноB2(2);рассмотреть… B1(N); раскруткуB2(N); первого цикла и в этой раскрутке выполнить перестановки соседних блоков: B1(I) и B2(I) для каждого I = 1,…,N.

ПереставляемыеB2(1); B1(1); соседниеB2(2); блокиB1(2);неимеютB2(N);междуB1(N);собой информационных зависимостей, поскольку в противном случае в исходном цикле были бы циклически независимые зависимости. В результате перестановки получилась раскрутка второго цикла, которая второму циклу эквивалентна.

Это преобразование2.4.4заменяет. Расщ плениецикл цикла

FOR (I=1; I<=N; I++); {

на фрагментТЕЛОЦИКЛАпрограммы(I); }

FOR (I=1; I<=M; I++); {

ТЕЛОЦИКЛА(I); }

FOR (I=1; I<=N-M; I++); {

Это жеТЕЛОЦИКЛАпреобразование(I+M);может} выглядеть и по-другому, как замена цикла

FOR (I=1; I<=N; I++); {

на фрагментТЕЛОЦИКЛАпрограммы(I); }

FOR (I=1; I<=M; I++); {

ТЕЛОЦИКЛА(I); }

FOR (I=M+1; I<=N; I++); {

(ЗдесьТЕЛОЦИКЛАне выполнена(I); канонизация} второго цикла в результирующем фрагменте.)

57

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

Расщепление цикла используется как вспомогательное преобразование. Например, если цикл состоит из 100 итераций и надо сделать ему 3-кратную развёртку, то этот цикл следует разделить на два: у одного 99 итераций, а у другого – 1.

Пример 54. Цикл

FOR (I=1; I<=100; I++); {

эквивалентенX[I]=Y[Iфрагменту-1]; } программы

FOR (I=1; I<=99; I = I+3); { X[I]=Y[I-1];

X[I+1]=Y[I]; X[I+2]=Y[I+1]; }

FOR (I=100; I<=100; I++); { КонецX(I)=Y(Iпримера-1);. }

Теорема 14. Предположим, что в цикле

FOR (I=1; I<=N; I++); {

естьТЕЛОЦИКЛАзависимые(I);индексные} переменные, у которых в индексных выражениях коэффициенты при счётчиках цикла имеют разные знаки: X(a*I+N1) и X(N+N2-b*I). Тогда в результате расщепления пространства итераций цикла в точке M = (N+N2-N1)/(a+b) получатся два цикла, в каждом из которых исходные вхождения не связаны зависимостью (информационная зависимость стала между циклами).

При выполнении расщепления следует контролировать соблюдение корректности. При копировании тела цикла не должно появиться двух операторов с одинаковыми метками. Оба результирующих цикла следует взять в операторные скобки, и если у

58

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

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

2.4.5. Разрыв итераций, гнездование цикла

Разрыв итерацийподобные(strip mining)им преобразования– это преобразование [2, с. 37],

которое заменяет цикл

FOR (I=1; I<=N; I++); {

на следующийТЕЛОЦИКЛАфрагмент(I); } программы:

FOR (I=1; I<=N//h; I++); { FOR (J=1; J<=h; J++);{ ТЕЛОЦИКЛА(h*(I-1)+J);} }

FOR (I=1; I<=N-(N//h)*h; I++); {

ЕслиТЕЛОЦИКЛАN делится((N//h)*h+I)на h, то разрыв; }итераций выглядит более просто. Это преобразование заменяет цикл

FOR (I=1; I<=N; I++); {

на следующийТЕЛОЦИКЛАцикл(I);: }

FOR (I=1; I<=N; I = I+h); {

FOR (J=1; J<=h; J++);{

99 ТЕЛОЦИКЛАГнездование(I+Jцикла-1);}похоже} на разрыв итераций и заменяет цикл

FOR (I=1; I<=N; I++); {

на следующийТЕЛОЦИКЛАфрагмент(I); } программы:

FOR (J=1; J<=h; J++); {

59

2. Преобразования программ

FOR (I=1; I<=N//h; I++);{ ТЕЛОЦИКЛА(I+(J-1)*(N//h));} }

FOR (I=1; I<=N-(N//h)*h; I++); {

ЕслиТЕЛОЦИКЛАN – константа((N//h)*h+I);и не делится} нацело на h, то ко второму циклу полученного фрагмента программы можно применить раскрутку цикла.

Метка последнего оператора тела цикла должна остаться только у оператора последней копии тела цикла. Если ТЕЛОЦИКЛА(I) содержит помеченные операторы, то в копиях тела цикла должны вводиться новые метки (вместе со ссылками на эти метки). Ввиду этого требования удобно, чтобы последним оператором тела цикла был CONTINUE (в языке FORTRAN) и чтобы не было меток у операторов без необходимости.

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

Не должно быть операторов выхода из цикла EXIT, BREAK. Разрыв итераций и гнездование цикла являются

равносильнымиТеорема 15.преобразованиями.

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

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

К преобразованиям «разрыв итераций» и «гнездование цикла» можно присовокупить ещё два, на них похожих. Первое из них меняет цикл

60

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]