Участник:Bannikov Nikolay/Параллельный алгоритм вычисления всех частных сумм
Свойства и структура алгоритма
Общее описание алгоритма
Данный алгоритм предназначен для вычисления всех частных сумм заданного массива данных. Для входного массива:
требуется найти массив:
такой, что каждый его элемент [math]\displaystyle{ S[i] }[/math] равен сумме всех элементов [math]\displaystyle{ X }[/math] от первого до [math]\displaystyle{ i }[/math]-го включительно.
Алгоритм относится к классу алгоритмов сдваивания. Его ключевая идея заключается в том, что на каждом шаге расстояние, на которое передаются и суммируются уже накопленные частичные результаты, удваиваются.
Входные данные представляют собой массив числовых элементов, для которых определена операция сложения, однако алгоритм может быть обобщен на любую ассоциативную бинарную операцию (умножение, нахождение минимума/максимума, логическое И/ИЛИ и.т.д.).
Алгоритм широко используется в параллельных вычислениях.
Математическое описание алгоритма
Пусть дан массив чисел:
Необходимо найти массив
где [math]\displaystyle{ s_i = x_1 + x_2 + ... + x_i }[/math] для всех [math]\displaystyle{ i }[/math] от 1 до n.
Алгоритм выполняется за [math]\displaystyle{ k = log_{2} n }[/math] шагов:
- Каждый процессор [math]\displaystyle{ i }[/math] хранит значение [math]\displaystyle{ s_i = x_i }[/math]
- Для каждого шага [math]\displaystyle{ t }[/math] от 1 до k:
- Вычисляем расстояние [math]\displaystyle{ d = 2^{(t-1)} }[/math]
- Для каждого индекса [math]\displaystyle{ i \gt d }[/math] процессор [math]\displaystyle{ i }[/math] выполняет: [math]\displaystyle{ s_i = s_i + s_{i-d} }[/math]
- Для каждого индекса [math]\displaystyle{ i ≤ d }[/math] значение [math]\displaystyle{ s_i }[/math] остается неизменным
- После [math]\displaystyle{ k }[/math] шагов массив [math]\displaystyle{ S }[/math] содержит все частные суммы
Простой пример для [math]\displaystyle{ n = 4: }[/math]
- Начало: [math]\displaystyle{ [x_1,x_2,x_3,x_4] }[/math]
- Шаг 1 (d=1): [math]\displaystyle{ [x_1, x_1 + x_2, x_2 + x_3, x_3 + x_4] }[/math]
- Шаг 2 (d=2): [math]\displaystyle{ [x_1, x_1 + x_2, x_1 + x_2 + x_3, x_1 + x_2 + x_3 + x_4] }[/math]
Вычислительное ядро алгоритма
Вычислительное ядро параллельного алгоритма вычисления всех частных сумм представляет собой операцию:
где:
- [math]\displaystyle{ s_i }[/math] - текущее значение в процессоре [math]\displaystyle{ i }[/math]
- [math]\displaystyle{ s_{i-d} }[/math] - значение из процессора, находящегося на расстоянии [math]\displaystyle{ d }[/math] слева
- [math]\displaystyle{ d }[/math] - расстояние между процессорами, которое удваивается на каждом шаге
Характеристики вычислительного ядра:
- Простота: Каждая операция - одно сложение
- Регулярность: На каждом шаге выполняется одинаковый тип операций, только меняется расстояние
- Параллельность: Все операции на одном шаге могут выполняться одновременно разными процессорами
- Коммуникационная интенсивность: Основная сложность - в организации быстрого обмена данными между процессорами, а не в самих вычислениях
На первом шаге выполняется [math]\displaystyle{ n - 1 }[/math] сложений, на втором [math]\displaystyle{ n - 2 }[/math] сложений и так далее. Несмотря на то, что общее количество операций [math]\displaystyle{ (n · log_{2} n) }[/math] больше чем в последовательном алгоритме [math]\displaystyle{ n }[/math] операций, время выполнения сокращается с [math]\displaystyle{ n }[/math] шагов до [math]\displaystyle{ log_{2} n }[/math] шагов благодаря параллелизму.
Макроструктура алгоритма
Алгоритм вычисления всех частных сумм не использует в качестве составных частей другие сложные алгоритмы, но может быть представлен как последовательность однотипных макрошагов (или итераций). Каждый макрошаг состоит из двух базовых операций: коммуникационной (передача данных) и вычислительной (суммирование).
Алгоритм представляет собой последовательность [math]\displaystyle{ log_{2} n }[/math] итераций.
Каждая итерация для всех активных процессоров представима следующими двумя этапами:
- Передача данных на расстояние [math]\displaystyle{ d }[/math]
- Сложение: [math]\displaystyle{ s[i] = s[i] + }[/math] полученное_значение
Схема реализации последовательного алгоритма
Для заданного массива чисел [math]\displaystyle{ X = [x_1, x_2,...,x_n] }[/math] требуется вычислить массив частичных сумм [math]\displaystyle{ S = [s_1,s_2,...,s_n] }[/math], где
В последовательном алгоритме частичные суммы можно вычислить весьма просто из рекуррентного соотношения:
Фрагмент кода на С++:
// Вычисление частичных сумм
s[0] = a[0];
for (int i = 1; i < n; i++)
{
s[i] = s[i - 1] + a[i];
}
- Последовательный алгоритм выполняет [math]\displaystyle{ O(n) }[/math] операций за [math]\displaystyle{ O(n) }[/math] времени на 1 процессоре
- Параллельный алгоритм выполняет [math]\displaystyle{ O(n · log n) }[/math] операций за [math]\displaystyle{ O(log n) }[/math] времени на [math]\displaystyle{ n }[/math] процессорах
- При [math]\displaystyle{ n = 1 }[/math] или при выполнении на одном ядре параллельный алгоритм вырождается в последоватьльный
Последовательная сложность алгоритма
Информационный граф
Ресурс параллелизма алгоритма
Входные и выходные данные алгоритма
Свойства алгоритма
Программная реализация алгоритма
Литература
- А.В.Старченко, В.Н.Берцун. Методы параллельных вычислений. Изд-во Том. ун-та, 2013. - 223 с. (Серия "Суперкомпьютерное образование")