Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Алгоритмы решения систем линейных уравнений с блочно-ленточными матрицами. Монография
.pdf
Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 2 31
Рисунок 17. Блочная двухдиагональная верхнетреугольная матрица.
Пример 3. Рассмотрим тесное гнездо двух циклов:
for (j = 0; j < m; j++) {
for (k = 0; k < j; k++) {
LoopBody(j,k);
}}
Пространство итераций этого гнезда является треугольником, поэтому такой двойной цикл иногда называют «треугольным».
Конец примера.
Поскольку граница внутреннего цикла не является константной на эта-
пе компиляции, оптимизирующие компиляторы не могут делать раскрутку
таких циклов непосредственно.
Сначала делаем раскрутку внешнего цикла. В результате получится код
в виде последовательности копий тела внешнего цикла, в которых вместо
счетчика внешнего цикла подставлены его значения. В каждой копии тела
внешнего цикла есть одномерный цикл, который ранее был внутренним
циклом, но у этого цикла уже после подстановки границы становятся константными. Теперь следует сделать раскрутки каждого из этих циклов.
Таким образом, раскрутка двойного треугольного цикла сводится к рас-
круткам одномерных циклов.
При численных методах решения краевых задач в трехмерных областях
могут возникать тройные пирамидальные циклы. Раскрутку таких гнезд циклов можно делать аналогично, начиная с внешнего (объемлющего) цикла.

32 Б. Я. Штейнберг, О. Б. Штейнберг
Оптимизирующие компиляторы не всегда делают раскрутку цикла. Эксперименты показали отсутствие раскрутки в GCC (вручную сделанная раскрутка ускоряет код в 2 раза).
Подстановка и вынос вычисления подвыражений
Подстановка — это преобразование, которое похоже на одноименное
преобразование в алгебре: если переменная равна какому-то выражению,
то всюду вместо этой переменной можно подставлять равное ей выражение.
В программе подстановка является эквивалентным преобразованием не
всегда, условия эквивалентной применимости формулируются в терминах
информационных зависимостей.
Пример 4. Подстановкой можно из кода
x = a+b;
...
y = x*c;
z = x-d;
получить следующий:
x = a+b;
...
y = (a+b)*c;
z = a+b-d;
Конец примера.
(2)
(3)
Вынос вычисления подвыражений — это преобразование, обратное к
подстановке. Например, это преобразование может из фрагмента кода (3)
получить (2). Один из фрагментов (2) или (3) может работать быстрее, а
другой — медленнее. Фрагмент (2) имеет меньше вычислительных операций, а фрагмент (3) иногда может иметь меньше переменных (если в
данном примере исключается переменная x, а переменные a и b уже присутствовали в преобразуемом фрагменте) и использовать меньше обменов
между регистрами и памятью (м. б. кэш-памятью). Количество свободных
регистров при выполнении фрагмента кода зависит от контекста (окружения этого фрагмента кода).
Всегда дает ускорение частный случай подстановки — протягивание
констант (правая часть подставляемого выражения — константа). Вынос
вычисления подвыражений из цикла (вынос инвариантных вычислений)
практически всегда дает ускорение.

Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 2 33
Гнездование цикла
Гнездование цикла — это преобразование с параметром p, который ино-
гда обозначает количество вычислительных устройств, участвующих в вычислении цикла.
Гнездование цикла (в предположении, что количество итераций цикла
делится нацело на параметр p) заменяет цикл
for (j = 0; j < N; j++) {
LoopBoby(j);
}
двойным гнездом циклов
for (j1 = 0; j1 < N/p; j1++) {
for (j2 = 0; j2 < p; j2++) {
j = (N/p)*j1+j2;
LoopBoby(j);
}}
или следующим двойным гнездом циклов
for (j1 = 0; j1 < p; j1++) {
for (j2 = 0; j2 < N/p; j2++) {
j = p*j1+j2;
LoopBoby(j);
}}
Выбор, каким из двух гнезд циклов следует заменить исходный цикл,
зависит от того, где нужен цикл с p итерациями: внутренним циклом гнезда
или внешним. Например, если предполагается заменить внутренний цикл
гнезда командой векторизатора, обрабатывающего векторы длины p, то нужен внутренний цикл длины p (такое преобразование рассматривается далее); если предполагается выполнять цикл на p асинхронных вычислительных устройствах (вычислительных ядрах процессора или узлах кластера),
следует внешний цикл гнезда получить длиной p.
При использовании тайлинга параметр p может быть размером тайла
(блока).
Если количество итераций исходного цикла не делится нацело на пара-
метр гнездования, то к результирующему двойному гнезду циклов дописывается еще одномерный цикл, вычисляющий несколько итераций (остаток
от деления числа N на число p).

34 Б. Я. Штейнберг, О. Б. Штейнберг
Тайлинг
Тайлинг — это преобразование программ, переводящее к блочным
вычислениям [18; 25; 26; 27; 28; 29; 48; 50] (слово тайл (tile) означает
черепица).
Идея перехода к блочным вычислениям (тайлинга) состоит в том, чтобы разбить программу на малое количество таких частей, вычисление каждой из которых использует множество данных, которые помещаются в
кэш-памяти.
Самый простой пример тайлинга — блочное умножение матриц. Если
умножаются матрицы размера N*N и матрицы разбить на квадратные
блоки m*m, то блочных строк и столбцов у этих матриц получается N/m.
Параметр m выбирается таким, чтобы в кэш-памяти поместилось 3 блока:
два перемножаемых и результирующий. Тогда при таком блочном алгоритме количество умножений (и сложений) чисел будет равно N3 (не зависит
от m), а количество обращений к оперативной памяти (долгая операция)
будет N3/m (количество обращений к кэш-памяти N3).
Тайлинг применяется только к тесным гнездам циклов.
Не к любому тесному гнезду циклов можно применить тайлинг.
Переход к тайлингу можно представить как суперпозицию двух преобразований: «гнездование цикла» и «перестановка циклов» (точнее, заголовков циклов). Условие перестановки циклов сложно формулируется в
терминах информационных зависимостей [60]. Такие зависимости не описываются графом информационных связей. Для описания таких зависимостей можно воспользоваться решетчатыми графами [21; 61].
Если к гнезду циклов непосредственно можно применить тайлинг, то
тайлы (фрагменты пространства итераций) получаются прямоугольниками (для двойного гнезда циклов) или прямоугольными параллелепипедами
(для тройного гнезда циклов).
Во многих случаях для применения тайлинга приходится предварительно линейно преобразовывать (скашивать) пространство итераций
гнезда циклов, что преобразует информационные зависимости к виду,
не мешающему тайлингу. Изменение пространства итераций происходит за счет изменения гнезда циклов. Если после такого преобразования
вернуться к исходному пространству итераций обратным линейным преобразованием, то тайлы окажутся параллелепипедами (в случае тройного гнезда циклов) непрямоугольными (скошенными). Такой скошенный тайлинг (skewed tiling) применим к значительно более широкому

Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 2 35
множеству гнезд циклов, которые встречаются в практически значимых
задачах [18].
Чтобы тайлинг был эффективен, желательно, чтобы количество циклов
гнезда было больше, чем наибольшая размерность массивов, входящих
в это гнездо. Например, программа перемножения матриц представляется тройным циклом, а входящие в него массивы (матрицы) двумерные.
Другой пример — программа, вычисляющая свертку двух векторов: циклов в гнезде 2, а входящие в гнездо массивы одномерные (описывающие
векторы).
Сочетание тайлинга и распараллеливания (параллельное выполне-
ние тайлов) дает высокое ускорение, иногда в десятки раз [18; 25; 26;
27; 28; 29].
Линеаризация выражений
Во многих случаях для ускорения программы возникает задача упро-
щения выражений. Особенно часто такая задача возникает для оптимизации индексных выражений после некоторых оптимизирующих преобразований, таких как гнездование цикла или тайлинг. Понятие «упрощение
выражения» может иметь неоднозначное толкование. Например, какое из
следующих двух равных выражений
a*b + a*c + d*c = a*(b + c) + d*c
считать более простым — левое или правое?
Левое представлено однородной формулой, а правое выражение содер-
жит меньше вычислительных операций.
Индексные выражения массивов желательно представить как аффинные
выражения относительно счетчиков циклов. Это необходимо для определения информационных зависимостей. В частности, линеаризация выполняет так называемые смешанные вычисления — операции с константами на
этапе компиляции.
Пример 5. Выражение X[(j + 1) + j + 2] заменяется выражением
X[(2* j + 3].
Линеаризация может быть уместна, например, при приведении тела
рекуррентного цикла к некоторому шаблонному виду.
Конец примера.

36 Б. Я. Штейнберг, О. Б. Штейнберг
Пример 6. Программный цикл
for (j = 1; j < N+1; j++) {
X[j] = X[j-1] + A[j]*X[j-1] + B[j];
}
можно привести к виду
for (j = 1; j < N+1; j++) {
X[j] = (A[j] + 1)*X[j-1] + B[j];
}
Конец примера.
2.2. Предкомпилятор для ускорения алгоритмов
Предкомпилятор — это конвертор, преобразующий код перед компиляцией.
Причин для разработки предкомпилятора может быть несколько.
Одна из причин, оправдывающая затраты на разработку предкомпилятора, состоит в том, что современные оптимизирующие компиляторы могут не очень эффективно оптимизировать.
Еще одной причиной может быть наличие параметров в программе, значение которых заранее не известно. Предположим, что в программе имеются циклы, в теле которых небольшое количество операций, а количество
итераций невелико, но заранее не известно. Например, размерность блока
блочно-ленточной матрицы.
Предкомпилятор должен преобразовать код к такому виду, который быстро выполняется после компиляции даже без оптимизации.
Для решателя СЛАУ с блочно-ленточной матрицей одним из неизвестных заранее параметров может быть размерность блоков, стоящих по диагоналям. Эта размерность может определяться, например, либо количеством неизвестных функций в системе дифференциальных уравнений, из
которой получена СЛАУ при использовании сеточного метода, либо количеством связей между элементами при методе конечных элементов.
Для одного из решателей пакета ACELAN-COMPOS сделан
SourceToSource конвертор (компилятор) [62] основанный на ОРС (Оптимизирующей распараллеливающей системы www.ops.rsu.ru), выполняющий
раскрутки двойных циклов, предварительно выполняя вынос инвариантных вычислений.

3. О ПАРАЛЛЕЛЬНЫХ ПРЯМЫХ
АЛГОРИТМАХ РЕШЕНИЯ СЛАУ С ЛЕНТОЧНЫМИ
И БЛОЧНО-ЛЕНТОЧНЫМИ МАТРИЦАМИ
В данной главе рассматриваются параллельные алгоритмы решения
систем линейных уравнений с ленточными матрицами. К решению таких
систем уравнений сводится вычисление программных циклов с линейной
рекуррентной зависимостью. Для более полного ознакомления с параллельными численными методами решения систем линейных алгебраических уравнений можно порекомендовать обзор [7; 8], а также соответствующие главы из книг [5; 15; 19].
Матрица A = { aij} размера n*n называется ленточной, если существуют
такие натуральные числа b и c, значительно меньшие n, что для любых i, j
таких, что i – j < – b или i – j > c, выполняется условие aij = 0. Если b и c —
наименьшие такие числа, то число d = b + c называется шириной ленты.
В памяти компьютера такая матрица представляется в виде одномерных
массивов, в которых хранятся элементы ненулевых диагоналей.
Иногда от матриц будем требовать условие диагонального преоблада-
ния. Напомним, что матрица A имеет диагональное преобладание, если
в любой строке модуль диагонального элемента больше суммы модулей
остальных элементов строки. Для СЛАУ с трехдиагональной матрицей и
преобладанием по главной диагонали предлагается метод параллельной редукции [14], а обобщение диагонального преобладания на случай блочных
диагоналей и условия устойчивой сходимости описаны в [15].
Следует отметить, что на компьютерах прошлых лет программные реа-
лизации рассматриваемых алгоритмов не могли быть эффективны. Критика
многих старых параллельных алгоритмов линейной алгебры приводится в
[21]. В обзорах [7; 8; 21] упоминаются такие алгоритмы, которые не только не учитывали время доступа к данным, но и использовали количество
вычислительных устройств, пропорциональное или большее количества
вычислительных операций.

38 Б. Я. Штейнберг, О. Б. Штейнберг
ha
3.1. Параллельное решение СЛАУ
с двухдиагональными и блочными
двухдиагональными матрицами
Будем рассматривать систему уравнений с ленточной двухдиагональной
матрицей следующего вида
Ясно, что эта система имеет единственное решение тогда и только
тогда, когда все элементы главной диагонали отличны от нуля. В этом
случае каждое i-ое уравнение можно разделить на a
можно выполнить одновременно. В результате задача сведется к решению системы уравнений с матрицей следующего вида с обновленными
значениями bi и hi.
00
hab
111
hab
222
.
hab
333
.......
hab
−−− 111
nnn
. Все эти деления
i
1
1
1
1
h
0
hb
11
hb
22
(4)
hb
33
.......
1
hb
−− 11
nn
Рассмотрим параллельный алгоритм решения такой системы уравнений. На первом шаге следует одновременно получить нули на месте элементов b
. Для этого из каждой i-ой строки (кроме первой) надо вычесть
i
предыдущую, умноженную на число bi. При этом все элементы поддиагонали станут нулевыми, но элементы второй поддиагонали окажутся
ненулевыми.

Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 3 39
00000001
00000001
h
0000001
hb
0000010
hb
0000100
hb
0001000
hb
0010000
hb
0100000
hb
1000000
hb
0
11
22
33
~ ~
44
55
66
77
h
0
00000010
h
1
0000010
hb
22
0000100
hb
33
0001000
hb
44
0010000
hb
55
0100000
hb
66
1000000
hb
77
На втором шаге из каждой i-ой строки (кроме первых двух) надо вы-
честь строку с номером (i – 2), умноженную на число b
. При этом все эле-
i
менты второй поддиагонали станут нулевыми, но элементы в четвертой
поддиагонали окажутся ненулевыми.
00000001
h
00000001
h
0
00000010
h
1
00000100
h
2
00001000
h
3
0001000
hb
44
0010000
hb
55
0100000
hb
66
1000000
hb
77
~ ~
На k-ом шаге из каждой i-ой строки (кроме первых 2
строку с номером i – 2
поддиагонали с номером 2
k – 1
, умноженную на число bi. При этом все элементы
k –1
станут нулевыми, но элементы поддиагонали
k –1
) надо вычесть
0
00000010
h
1
00000100
h
2
00001000
h
3
00010000
h
4
00100000
h
5
01000000
h
6
10000000
h
7
с номером 2k окажутся ненулевыми. Через ]log(n)[ шагов матрица системы
станет единичной, и в правой части будет получен вектор-решение.
Здесь и далее логарифмы берутся по основанию 2 и операция ]a[ озна-
чает наименьшее целое, не меньшее числа a.
Заметим, что в этом алгоритме ненулевая поддиагональ после каждо-
го шага оказывается вдвое дальше от главной диагонали, чем перед этим
шагом.
Для верхнетреугольных двухдиагональных матриц алгоритм решения
системы уравнений выглядит аналогичным образом.

40 Б. Я. Штейнберг, О. Б. Штейнберг
00
11
22
bh
bh
bh
11
22
33
11
.......
nn
H
BH
BH
BH
BH
−−
1
1
1
1
.
h
3
. . . . .. .
h
1
1
n
−
Будем рассматривать системы линейных уравнений следующего вида:
1
0
1
1
.
1
1
Здесь элементами n*n матрицы являются матрицы (блоки) размера
m*m. Координатами вектора правой части системы являются векторы дли-
ны m. Параллельный алгоритм решения такой системы уравнений аналогичен алгоритму в скалярном случае.
3.2. О последовательном алгоритме,
соответствующем параллельному алгоритму
решения СЛАУ с двухдиагональной матрицей
Выше описан параллельный алгоритм решения СЛАУ с двухдиагональной матрицей. При создании программ, основанных на этом алгоритме,
могут возникать нелегко обнаруживаемые ошибки. Остановимся на этой
проблеме подробнее, иллюстрируя ее на скалярном случае, хотя все рассуждения верны и для блочной двухдиагональной матрицы.
Во многих системах параллельного программирования, например в
OpenMP, распараллеливаются циклы. Поэтому интересно найти такой последовательный алгоритм, распараллеливание цикла в котором приведет
к описанному ранее параллельному алгоритму решения СЛАУ с двухдиагональной матрицей. Попытаемся записать последовательную программу,
соответствующую этому алгоритму.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
