Опубликован: 22.04.2008 | Доступ: свободный | Студентов: 526 / 50 | Оценка: 4.50 / 4.75 | Длительность: 06:55:00
Специальности: Программист
Лекция 3:

Высокоуровневый язык параллельного программирования MC#

< Лекция 2 || Лекция 3: 123 || Лекция 4 >

Вычисление частичных сумм массива

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

Рассмотрим задачу вычисления частичных сумм массива 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, приведен ниже.


3.6. Сведения о практической реализации языка MC#

Как обычно, для любого параллельного языка программирования, реализация MC# состоит из компилятора и рантайм-системы. Главными функциональными частями рантайм-системы являются:

  1. ResourceManager - процесс, исполняющийся на центральном узле и распределяющий по узлам movable-методы.
  2. WorkNode - процесс, исполняющийся на каждом из рабочих узлов и контролирующий выполнение movable-методов.
  3. Communicator - процесс, исполняющийся на каждом из узлов и ответственный за принятие сообщений для объектов, расположенных на данном узле.

Компилятор переводит программу из MC# в C#, его главной целью является создание кода, реализующего:

  1. выполнение movable-методов на других процессорах,
  2. пересылку канальных сообщений и
  3. синхронизацию методов, объединённых связкой.

Эти функции предоставляются соответствующими методами классов рантайм-системы. Среди них:

  1. класс Session - реализует вычислительную сессию.
  2. класс TCP - предоставляет возможность доставки запросов на исполнение movable-методов и канальных сообщений.
  3. класс Serialization - предоставляет сериализацию/десериализацию объектов, перемещаемых на другие рабочие узлы.
  4. класс Channel - содержит информацию о канале.
  5. класс Handler - содержит информацию об обработчике.

Главные функции компилятора MC#:

  1. Добавление вызовов функций Init() и Finalize() класса Session в главном методе программы. Функция Init() доставляет исполняемый модуль программы на другие узлы, запускает процесс Manager, создаёт объекты LocalNode и другие. Функция Finalize() останавливает запущенные потоки и завершает вычислительную сессию.
  2. Добавление выражений, создающих объекты типа сhannel и handler для каждого из каналов и обработчиков, описанных в программе.
  3. Замена вызовов async-методов на порождение соответствующих локальных потоков.
  4. Замена вызовов movable-методов на запросы менеджеру распределения ресурсов.
  5. Замена канальных вызовов на пересылку соответствующих сообщений по TCP-соединению. Трансляция связок, содержащих определения каналов, производится так же, как и в языке Polyphonic C#.
< Лекция 2 || Лекция 3: 123 || Лекция 4 >