Кластерные вычисления

Разработка сложных параллельных программ на языке MC#

Показывать лекцию целиком

Цель - изучить параллельный алгоритм вычисления частичных сумм массива чисел и реализовать его на языке MC#.

На данном занятии, требуется реализовать программу на языке MC#, решающую задачу вычисления частичных сумм массива f длины n.

А именно, по заданному массиву чисел f [ 0 : n-1 ] необходимо построить массив h [ 0 : n-1 ], такой что

$$h[j]=\sum\limit_{i=0}^j f[i]\quad,\quad\mbox{для каждого }j:\; 0\le j<n$$

Идея параллельного решения этой задачи состоит в разбиении массива f на p сегментов, где n кратно p, с дальнейшей одновременной обработкой этих сегментов данных длины m = n div p. Таким образом, обработка каждого сегмента будет производиться movable -методом.

(Отметим, что приведенное ниже решение пригодно и для случая, когда n не кратно p. Соответствующее обобщение может рассматриваться в качестве упражнения).

Разбиение исходного массива f на p сегментов производится таким образом, что в сегмент q, где ( 0 <= q < p ) попадают элементы f [ i ], такие что i mod p = q.

Так, например, если n = 16 и p = 4, то

0 -ой сегмент составят числа f [ 0 ], f [ 4 ], f [ 8 ], f [ 12 ] ;

1 -ый сегмент составят числа f [ 1 ], f [ 5 ], f [ 9 ], f [ 13 ]

и т.д.

Параллельный алгоритм вычисления частичных сумм будет устроен так, что q -му процессу ( movable -методу), обрабатывающему q -ый сегмент данных, достаточно будет общаться лишь с его соседями слева и справа (соответственно, 0 -му процессу - лишь с соседом справа, а последнему, (p-1) -му процессу - лишь с соседом слева) и главной программой для возврата результатов. Процесс с номером q будет вычислять все элементы h [j] результирующего массива, такие что j mod p = q, где 0 <= j < n.

Фрагмент главной программы, разбивающей исходный массив на сегменты и вызывающий movable -метод handleSegment, показан ниже. Здесь первым аргументом этого метода является номер сегмента, а последним - имя канала для возврата результатов.

Объекты класса BDChannel объявляются следующим образом :

Схема взаимодействия процессов ( movable -методов) между собой и главной программой показана ниже:

После разбиения, исходный массив f приобретает вид двумерной матрицы, распределенной по p процессам:

процесс 0:a0,0 a0,1 a0,m-1
процесс 1:a1,0 a1,1 a1,m-1
...
процесс q:aq,0 aq,1 aq,m-1
процесс p-1:ap-1,0 ap-1,1 ap-1,m-1

Другими словами, эта матрица получена из массива f разрезанием его на p сегментов и транспонированием каждого сегмента.

Ключевая идея алгоритма отдельного процесса q состоит в заполнении локальных для него массивов h0 и h1 (оба, имеющие размерность m ) в соответствии с формулами:

$$h0[i]=\sum\limits_{j=0}^{q-1}\sum\limits_{k=0}^i a_{j,k}\quad,\quad 0\le i<m,$$ $$h1[i]=\sum\limits_{j=q+1}^{p-1}\sum\limits_{k=0}^{i-1}a_{j,k}\quad,\quad 0\le i<m.$$

Неформально, это означает, что для процесса с номером q i -ый элемент массива h0 есть сумма всех элементов приведенной выше матрицы, которые расположены выше и слева элемента aq,i (включая и элементы столбца i ).

Аналогично, i -ый элемент массива h1 есть сумма всех элементов матрицы, которые расположены ниже и слева элемента aq,i (но, не включая элементов из столбца i ).

Ниже показана иллюстрация этого принципа для n = 16, p = 4 и q = 1, i = 2.

После того, как вычислены массивы h0 и h1 (посредством взаимодействия с соседними процессами), процесс с номером q может вычислить элемент h[i * p + q] результирующего массива как

$$h0[i]+\sum_{j=1}^i a_{q,j}+h1[i]\quad,\quad \mbox{для всех } i:\;0\le i<m.$$

Получаемые результирующие m значений процесс q сохраняет в локальном массиве h для передачи их главной программе. Тогда общая схема movable -метода handleSegment выглядит следующим образом:

Фрагмент программы, вычисляющий массив h0, приведен ниже.

Вернуться к учебному плану