Высокоуровневый язык параллельного программирования MC#
Вычисление частичных сумм массива
В этом разделе демонстрируется более сложный пример использования обработчиков для организации конвейера между процессами, представленными movable -методами.
Рассмотрим задачу вычисления частичных сумм массива f длины n.
А именно, по заданному массиву чисел f [ 0 : n-1 ] необходимо построить массив h [ 0 : n-1 ], такой что
Идея параллельного решения этой задачи состоит в разбиении массива 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 ) в соответствии с формулами:
Неформально, это означает, что для процесса с номером 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] результирующего массива как
Получаемые результирующие m значений процесс q сохраняет в локальном массиве h для передачи их главной программе. Тогда общая схема movable -метода handleSegment выглядит следующим образом:
Фрагмент программы, вычисляющий массив h0, приведен ниже.
3.6. Сведения о практической реализации языка MC#
Как обычно, для любого параллельного языка программирования, реализация MC# состоит из компилятора и рантайм-системы. Главными функциональными частями рантайм-системы являются:
- ResourceManager - процесс, исполняющийся на центральном узле и распределяющий по узлам movable-методы.
- WorkNode - процесс, исполняющийся на каждом из рабочих узлов и контролирующий выполнение movable-методов.
- Communicator - процесс, исполняющийся на каждом из узлов и ответственный за принятие сообщений для объектов, расположенных на данном узле.
Компилятор переводит программу из MC# в C#, его главной целью является создание кода, реализующего:
- выполнение movable-методов на других процессорах,
- пересылку канальных сообщений и
- синхронизацию методов, объединённых связкой.
Эти функции предоставляются соответствующими методами классов рантайм-системы. Среди них:
- класс Session - реализует вычислительную сессию.
- класс TCP - предоставляет возможность доставки запросов на исполнение movable-методов и канальных сообщений.
- класс Serialization - предоставляет сериализацию/десериализацию объектов, перемещаемых на другие рабочие узлы.
- класс Channel - содержит информацию о канале.
- класс Handler - содержит информацию об обработчике.
Главные функции компилятора MC#:
- Добавление вызовов функций Init() и Finalize() класса Session в главном методе программы. Функция Init() доставляет исполняемый модуль программы на другие узлы, запускает процесс Manager, создаёт объекты LocalNode и другие. Функция Finalize() останавливает запущенные потоки и завершает вычислительную сессию.
- Добавление выражений, создающих объекты типа сhannel и handler для каждого из каналов и обработчиков, описанных в программе.
- Замена вызовов async-методов на порождение соответствующих локальных потоков.
- Замена вызовов movable-методов на запросы менеджеру распределения ресурсов.
- Замена канальных вызовов на пересылку соответствующих сообщений по TCP-соединению. Трансляция связок, содержащих определения каналов, производится так же, как и в языке Polyphonic C#.