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

Алгоритмы решения систем линейных уравнений с блочно-ленточными матрицами. Монография

.pdf
Скачиваний:
0
Добавлен:
06.09.2026
Размер:
2 Мб
Скачать
Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 1 21
описывается генерация параллельного кода с генерацией коммуникаций. Компилируются гнезда циклов с аффинными зависимостями. Об опти­мизации размещения данных и локализации данных ничего не говорится. «Распределение данных и распределение вычислений тесно связаны — из­менение распределения данных простым способом часто требует полной перезаписи вычислительного и коммуникационного кода». Преобразование последовательных программ для отображения на вычислительные системы с распределенной памятью требует разработки дополнительных функций, которых нет в системах с общей памятью: размещение данных в распре­деленной памяти, команды коммуникационной системе для генерации межпроцессорных пересылок. Для ВС с распределенной памятью самой длительной операцией становится межпроцессорная пересылка данных. Для высокопроизводительных кластеров такие пересылки могут погасить ускорение от распараллеливания и даже вызвать замедление. Для эффек­тивного отображения программы на такие ВС программа должна удовлет­ворять очень жестким требованиям. Создание таких программ — ручная работа, потому что распараллеливание компилятором может быть эффек­тивно для очень узкого класса программ, и оно теряет смысл. Но в по­следнее время появляются многоядерные процессоры, иногда называемые «суперкомпьютер на кристалле», с десятками, сотнями и тысячами ядер [39; 40]. Пересылка данных между процессорными ядрами на одной ми­кросхеме требует значительно меньше времени, чем на коммуникационной сети (Ethernet, Innibend, PCI-express). Это озна ча ет расширение множе­ства эффективно распараллеливаемых программ и делает целесообразным разработку распараллеливающих компиляторов. Следует отметить работы группы DVM-System [51] по генерации параллельного кода на ВС с рас­пределенной памятью. Генерирующие автоматически параллельный код на ВС с распределенной памятью компилирующие системы DVM, Parawise и др. предполагают дописывание прагм в текст последовательной программы без предварительного преобразования.
Время, необходимое на пересылку данных, нелинейно зависит от объ-
ема пересылаемых данных. Значительное время отнимает инициализация пересылки. Метод размещения данных с перекрытиями [26; 52] существен­но ускоряет параллельные итерационные алгоритмы за счет уменьшения количества пересылок при укрупнении множеств пересылаемых данных.
В [53] содержится много преобразований программ, которые могли бы
войти в оптимизирующие компиляторы.
22 Б. Я. Штейнберг, О. Б. Штейнберг
Поскольку оптимизация кода с помощью промышленных компиляторов отстает от возможностей современных процессоров, актуальным становит­ся вопрос о создании предкомпиляторов — высокоуровневых преобразова­телей программ к виду, из которого создается более эффективный бинар­ный код стандартным компилятором.
1.5. Информационные зависимости в программе
Преобразования программ, к сожалению, не обладают такими хороши­ми свойствами, как преобразования алгебраических или топологических объектов. Каждое преобразование применимо только к некоторому под­множеству программ. Преобразования носят в основном локальный ха­рактер, т. е. применяются к некоторому фрагменту программы. Создание программной реализации преобразований в компиляторе требует высокой квалификации программистов-разработчиков. Известные семейства ком­пиляторов с открытым кодом GCC и LLVM в своей структуре имеют много парсеров с разных языков, много генераторов бинарного кода на разные целевые процессоры и один общий блок оптимизирующих преобразований во внутреннем (промежуточном) представлении.
Приведем необходимые определения из теории преобразования про­грамм [44; 45; 53; 54; 55; 56; 57; 58; 59].
Анализ информационных зависимостей в программах используется для контроля эквивалентности преобразований. Основное требование к преоб­разованиям — эквивалентность. Две программы называют эквивалентны­ми, если они на одних и тех же входных данных выдают одинаковые ре­зультаты. (Возможны уточнения этого определения, связанные с областью входных данных программы и по поводу погрешностей выходных данных в формате вещественных чисел с плавающей точкой.) Преобразование называется эквивалентным, если исходная и результирующая программы эквивалентны.
Вхождением переменной, как обычно, будем называть всякое появление переменной в тексте программы вместе с ее местом в программе. Всяко­му вхождению (при конкретном значении индексного выражения для мас­сивов) соответствует обращение к некоторой ячейке памяти: чтение или запись. Если при этом обращении происходит запись в ячейку памяти, то такое вхождение называют генератором. Остальные вхождения (при обра­щении к которым данные читаются из памяти) называют использованиями.
Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 1 23
Два вхождения порождают информационную зависимость, если при не-
которых допустимых значениях индексных выражений они обращаются к одной и той же ячейке памяти.
Генератор обычно обозначается out (output), а использование — in (input). Рассматриваются четыре типа информационных зависимостей между
вхождениями переменных: out-in — истинная информационная зависи­мость (true dependence), in-out — антизависимость (antidependence), out­out — выходная зависимость (output dependence), in-in — входная зависи­мость [45; 54].
Рассматривается также тривиальная информационная зависимость —
зависимость между использованием оператора присваивания и генерато­ром этого же оператора. Смысл этой зависимости в том, что значение ис­пользования правой части оператора присваивания влияет на записываемое в память значение генератора этого же оператора.
Граф информационных связей — это ориентированный граф, верши-
нами которого являются вхождения, а дуги соединяют информационно за­висимые пары вершин (v, u), в которых вхождение v обращается к общей ячейке памяти раньше, чем u. Дуги обычно рассматриваются только истин­ной зависимости, антизависимости, выходной зависимости и тривиальные.
Дуги входной зависимости in-in, связывающие два использования, не
влияют на преобразования программ, но влияют на размещения массивов в распределенной памяти.
К зависимостям вхождений внутри цикла добавляются дополнительные
характеристики. Информационная зависимость между вхождениями на­зывается циклически независимой (loop independent dependence), если эти вхождения обращаются к одной и той же ячейке памяти на одной и той же итерации цикла. Иначе зависимость называется циклически порожденной (loop carried dependence) [45; 54].
Граф информационных связей является более «тонким», чем обычно
используемый при оптимизирующей компиляции и часто встречающий­ся в публикациях граф зависимостей по данным (data dependence graph). У графа зависимостей по данным вершинами являются не вхождения пе­ременных, а операторы программы. Если в графе информационных связей «склеить» (перейти к фактор-графу) все вершины, принадлежащие одному и тому же оператору (для каждого оператора), то получится граф зависи­мостей по данным. В данной работе при анализе циклов с рекуррентны­ми зависимостями рассматриваются информационные зависимости между вхождениями переменных иногда внутри одного оператора: для такого ана-
24 Б. Я. Штейнберг, О. Б. Штейнберг
лиза граф зависимостей по данным не подходит и необходим граф инфор­мационных связей.
1.6. Гнезда циклов и пространство итераций
Гнездо циклов — это фрагмент программы, содержащий циклы, для лю­бых двух из которых выполняется условие, что один вложен в другой.
Пример 1. Алгоритм решения СЛАУ A*x = h с нижнетреугольной мат рицей A и вектором правой части h.
for (i = 0; i < N; i ++;) { h[i] = h[i]/a[i,i]; for (j = i+1; j < N; j ++) { h[j] = h[j] – h[i]/a[i,j]; }}
Здесь цикл со счетчиком j вложен в цикл со счетчиком i.
Конец примера.
Гнездо циклов называется тесным (perfect loop nest), если телом каждо­го цикла, кроме последнего, является вложенный в него цикл. Программа решения СЛАУ с треугольной матрицей содержит нетесное гнездо циклов.
Для гнезда циклов можно составить вектор счетчиков циклов. В указан­ном примере для решения СЛАУ с треугольной матрицей вектор счетчиков циклов (i, j). Пространство итераций гнезда циклов — это множество зна­чений, которое принимает вектор счетчиков циклов. Пространство итера­ций — это некоторое множество в пространстве целочисленных векторов
Zn, где n — количество циклов в гнезде. Точки пространства итераций со-
ответствуют копиям гнезда циклов.
Для тесного гнезда циклов, у которого границы изменения счетчиков циклов аффинно зависят от счетчиков вышестоящих циклов, пространство итераций является выпуклым многогранником [21; 56; 57].
Бывает так, что ни один цикл гнезда не допускает распараллеливание (мешают информационные зависимости), но некоторые множества точек пространства итераций можно выполнять одновременно. Во многих таких случаях одновременно выполняемые точки пространства итераций лежат на некоторой гиперплоскости. Поиск таких гиперплоскостей можно выпол­нять «методом гиперплоскостей» [44].
Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 1 25
1.7. Блочно-ленточные матрицы
Хранение ленточных матриц в памяти
как разреженных и в одномерных массивах
Ленточные матрицы — это матрицы, у которых ненулевые элементы ле-
жат на нескольких диагоналях.
Ленточные матрицы относятся к классу разреженных матриц. Квадрат-
ную матрицу можно рассматривать как матрицу весов некоторого графа и хранить, соответственно, списком дуг (ребер). При списке дуг каждая дуга представлена тройкой чисел (i, j, x[i, j]), где целые i, j — это номера стро­ки и столбца, в которых хранится ненулевой элемент матрицы x[i, j] (тип double). Объем памяти для одного элемента списка дуг 4 + 4 + 8 = 16 byte.
Ленточную матрицу можно хранить диагоналями. Каждая диагональ —
это одномерный массив double. Объем памяти получается в 2 раза меньше, чем у разреженной матрицы общего вида.
Блочно-ленточные матрицы
Если рассматривается не одно дифференциальное уравнение, а система
дифференциальных уравнений на регулярной области, то при численном решении методом конечных разностей матрица СЛАУ получается блоч­но-ленточной (при подходящей нумерации узлов сетки). Элементы ма­трицы СЛАУ — блоки (матрицы), размерность которых равна количеству уравнений в исходной системе дифференциальных уравнений. Такие же матрицы возникают и при использовании метода конечных элементов.
Рисунок 14. Блочная пятидиагональная матрица.
26 Б. Я. Штейнберг, О. Б. Штейнберг
Хранение блочно-ленточной матрицы
Каждая блочная диагональ блочно-ленточной матрицы хранится в памя­ти одномерным массивом. Элементы каждого блока размещаются в опера­тивной памяти рядом построчно. Такое размещение приводит к минимиза­ции кэш-промахов.
Рисунок 15. Одна диагональ блочно-ленточной матрицы.
Иерархия блоков в блочно-ленточной матрице
Иногда блоки блочно-ленточной матрицы состоят, в свою очередь, из блоков меньшей размерности. Получается иерархия блоков. Какой уровень блоков матрицы (массива) использовать в параллельном алгоритме — вы­бирает пользователь.
Вычислительная система может иметь иерархию модулей памяти и не­сколько уровней распараллеливания. Например, вычислительный кластер может иметь много ПЭ, каждый из которых имеет свою оперативную па­мять, а каждый процессор имеет кэш-память нескольких уровней.
Иерархия блочности и блочно-рекуррентные размещения массивов
(в том числе в распределенной памяти)
Во многих практически важных случаях у блочно-ленточной матрицы блоки могут опять состоять из блоков меньшей размерности.
Например, если методом конечных разностей решается трехмерная краевая задача Дирихле в прямоугольной трехмерной области, то в матри­це СЛАУ могут быть блоки, размерность которых совпадает с одной или другой размерностями разностной сетки. Для конечноразностного метода двумерной задачи для системы дифференциальных уравнений появляют-
Алгоритмы решения СЛАУ с блочно-ленточными матрицами. Глава 1 27
ся блоки, связанные с одной из размерностей сетки, и блоки, размерность которых равна количеству неизвестных функций. Пользователь выбирает, какие блоки и как использовать.
Свертки и фильтры
Будем рассматривать операторы дискретной свертки и соответствую-
щие им матрицы.
for (j = 0; j < N; j++) {
for (k = 0; k < m; j++) {
y[j] += a[j-k]*x[k];
}}
В операторном виде свертку можно записать
y = A*x,
где A — оператор свертки с ядром — функцией a.
Если ядро оператора свертки определено на небольшом сегменте целых
чисел, то матрицы, соответствующие такому оператору свертки, имеют ленточный вид. По диагоналям такой матрицы оказываются одинаковые числа; такие матрицы называются Тёплицевыми. Для решения уравнений с такими матрицами существуют специальные методы, более быстрые, чем для ленточных матриц общего вида. В частности, может применяться пре­образование Фурье.
Если ядро свертки A является матрицей-функцией, то такому матрич-
ному оператору свертки соответствует блочно-ленточная матрица (блочная Тёплицева матрица). Двумерной свертке соответствует блочная Тёплице­ва матрица, блоками которой являются одномерные Тёплицевы матрицы. Аналогично могут рассматриваться многомерные свертки.
Одномерные свертки возникают в задачах цифровой фильтрации сигна-
лов, двумерные свертки — при обработке изображений, трехмерные — при обработке видеопотоков.
Свертки на конечной циклической группе приводят к циклической (или
циркулянтной) матрице. Такие свертки могут возникать, например, при вос­становлении смазанных изображений, получаемых вращающейся камерой.
Операторам свертки с переменными коэффициентами (операторами
умножения на функцию) соответствуют ленточные матрицы общего вида. Многомерным сверткам или матричным сверткам с переменными коэффи­циентами соответствуют блочно-ленточные матрицы общего вида.
28 Б. Я. Штейнберг, О. Б. Штейнберг
Циклические блочно-ленточные матрицы
Диагональ матрицы A состоит из всех элементов Aij, для которых (i – j) = k. Это позволяет по номеру строки элемента легко находить номер столбца.
Если у прямоугольника с прямоугольной сеткой склеить противополож­ные стороны, то получится боковая поверхность цилиндра с соответству­ющей сеткой на ней. Узлы и ребра такой сетки, лежащие на горизонтали, создают на матрице весов обобщенную диагональ, т. е. множество всех эле­ментов Aij, для которых (i j) = k mod n. Такая обобщенная диагональ тоже может компактно храниться в памяти в виде одного одномерного массива.
Рисунок 16. Циклическая матрица с одной обобщенной блочной диагональю.
2. ПРЕОБРАЗОВАНИЯ ПРОГРАММ
И ПРЕДКОМПИЛЯТОР ДЛЯ УСКОРЕНИЯ
РЕШЕНИЯ СЛАУ С БЛОЧНО-ЛЕНТОЧНЫМИ
МАТРИЦАМИ
2.1. Некоторые эквивалентные преобразования программ
В этом параграфе приводятся некоторые преобразования программ, из­вестные в оптимизирующей компиляции, которые используются в данной работе. Будем рассматривать циклы, содержащие счетчик с фиксирован­ным шагом, а также фиксированными границами. Многие преобразования программ и условия их применимости описаны, например, в [54; 55; 58; 59] и др.
Раскрутка цикла
Раскрутка цикла (loop full unrolling) — это замена цикла последователь- ностью копий его тела с соответствующими значениями счетчика цикла j.
Таким образом, цикл:
for (j = 0; j < N; j++) { LoopBoby(j); }
в результате применения преобразования «раскрутка цикла» преобразуется в следующий программный код:
LoopBoby(1); LoopBoby(2); ... LoopBoby(N);
иногда необходимо добавить присваивание j = N (если значение j исполь­зуется в дальнейшем программном коде).
Эквивалентность раскрутки определяется в описании семантики про­граммного цикла со счетчиком: выполнение оператора цикла состоит в
30 Б. Я. Штейнберг, О. Б. Штейнберг
повторении выполнения его тела при соответствующем заголовку цикла
изменении счетчика.
Преобразование программного цикла компилятором к низкоуровневому ассемблерному виду выглядит примерно (для разных компиляторов разные варианты) следующим образом:
j = 1; 88 if not (j < N) { goto 99; } LoopBoby(j); j = j+1; goto 88; 99 Next statement;
Таким образом, к вычислению каждой копии тела цикла добавляется вычисление инкремента счетчика цикла и вычисление условного операто­ра. Если тело цикла невелико, то доля добавленных операций может быть существенной.
Раскрутка цикла, с одной стороны, уменьшает количество вычислитель­ных операций, что приводит к ускорению, но, с другой стороны, увеличива­ет объем кода, что может приводить к замедлению выполнения программы. После компиляции в ассемблер двойные циклы имеют больше (в 2 раза!) лишнего кода. Поэтому раскрутку следует проводить для циклов с относи­тельно небольшим количеством операций. Особенно это относится к гнез­дам вложенных циклов.
Пример 2. Рассмотрим тесное гнездо двух циклов:
for (j = 0; j < m; j++) { for (k = 0; k < m; j++) { LoopBody(j,k); }}
Пространство итераций этого гнезда является квадратом, поэтому такой двойной цикл иногда называют «квадратным».
Конец примера.
В блочно-ленточных матрицах чтение элементов блока выполняется двойным циклом, каждый из которых имеет малое количество итераций. Если блок стоит на главной диагонали треугольной матрицы (после LU-раз­ложения), то этот блок тоже треугольный и обрабатывается треугольным двойным программным циклом. Раскрутка таких двойных циклов должна приносить хорошее ускорение.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]