1. Краткое введение в Microsoft Robotics
В состав Microsoft Robotics входит несколько компонент:
- библиотека Concurrent and Coordination Runtime (CCR) – предназначена для организации обработки данных с помощью параллельно и асинхронно выполняющихся методов. Взаимодействие между такими методами организуется на основе сообщений. Рассылка сообщений основана на использовании портов;
- Decentralized Software Services (DSS) – среда, которая позволяет запускать алгоритмы обработки данных на разных ЭВМ, организовывать асинхронное взаимодействие процессов управления различными подсистемами робота;
- Visual Simulation Environment (VSE) – среда визуализации, которая позволяет экспериментировать с моделями роботов, тестировать алгоритмы управления роботами;
- Visual Programming Language (VPL) – язык, предназначенный для разработки программ управления роботами. Программа на таком языке представляется в виде последовательности блоков, которые выполняют обработку данных, и связей между ними.
Основные понятия Microsoft Robotics:
- сервис – приложение, разрабатываемое в Microsoft Robotics;
- сообщение – экземпляр любого типа данных;
- сервис – приложение, разрабатываемое в Microsoft Robotics;
- порт – очередь сообщений типа FIFO (First-In-First-Out), сообщение остаётся в порте пока не будут извлечено из очереди порта получателем.
Определение порта:
Port<int> p = new Port<int>();
Отправка сообщения в порт:
p.Post(1);
- получатель – структура, которая выполняет обработку сообщений. Данная структура объединяет:
- один или несколько портов, в которые отправляются сообщения;
- метод (или методы), которые используются для обработки сообщений (такой метод называется задачей);
- логическое условие, определяющее ситуации, в которых активизируется тот или иной получатель.
Описание получателя:
Arbiter.Receive(<тип_получателя>, <порт>, <выполняемый_делегат>); Пример: Activate( Arbiter.Receive(false, intPort, delegate(int n) { Console.WriteLine("Получено сообщение: " + n.ToString()); } ) );
Делегат, входящий в получатель, выполнится, когда в порт intPort придёт сообщение.
Получатели сообщений бывают двух типов: временные и постоянные (в примере получатель – временный). Временный получатель, обработав сообщение (или несколько сообщений), удаляется из списка получателей сообщений данного порта. - процессом запуска задач управляет диспетчер. После выполнения условий активации задачи (одним из условий активации может быть получение портом сообщения) диспетчер назначает задаче поток из пула потоков, в котором она будет выполняться.
Описание диспетчера с двумя потоками в пуле:
Dispatcher d = new Dispatcher(2, "MyPool");
Описание очереди диспетчера, в которую задачи ставятся на выполнение:
DispatcherQueue dq = new DispatcherQueue("MyQueue", d);
2. Создание проекта сервиса
Проект сервиса создаётся в Visual Studio на закладке Microsoft Robotics (см. рис. 1).
Рис. 1. Создание проекта сервиса
После создания проекта нужно настроить сервис: указать для него имя, пространство имён, домен организации, для которой разрабатывается сервис, а также год и месяц создания сервиса. Домен и дата используются для создания уникального идентификатора сервиса (в данном проекте этот идентификатор непосредственно нужен не будет).
Рис. 2. Настройка сервиса
Проект сервиса состоит из нескольких файлов:
- ParallelProcessing.cs – ядро сервиса;
- ParallelProcessingTypes.manifest.xml – манифест, который используется DSS для загрузки сервиса;
- ParallelProcessingTypes.cs – содержит описание типов, которые используются сервисом.
После компиляции сервиса генерируется набор dll библиотек и манифест. Манифест – файл в XML формате, содержащий ссылки на сервисы, с которыми взаимодействует разрабатываемый сервис. Настройки проекта сервиса установлены так, что после компиляции сервиса созданные dll библиотеки копируются в каталог \bin.
Запуск сервиса выполняется с помощью программы dsshost, которая загружает манифест и все сервисы, которые в нём указаны.
3. Структура сервиса
В структуру кода сервиса входит метод Start, который вызывается, когда DSS узел (dsshost.exe) запускает сервис (поэтому в метод Start обычно помещают действия по инициализации данных):
protected override void Start() { base.Start(); // команды инициализации // сервиса добавляются ниже }
После компиляции проекта генерируются три dll библиотеки:
- ParallelProcessing.Y2013.M02.dll – библиотека реализации сервиса, формируемая на основе файлов исходного кода, включённых в проект сервиса;
- библиотека ParallelProcessing.Y2013.M02.proxy.dll позволяет использовать твой сервис другими сервисами;
- библиотека ParallelProcessing.Y2013.M02.transform.dll содержит описание соответствия между типами, определенными в реализации сервиса и реализации proxy библиотеки. Данная библиотека загружается автоматически с помощью среды выполнения DSS.
Выполнить созданный сервис можно не только через F5 в Visual Studio, но и через командную строку. Для этого нужно выбрать пункт «DSS Command Prompt» в меню Пуск\MRDS. В результате, запустится окно командного интерпретатора, будет установлен корневой каталог и переменные окружения. В открывшемся окне нужно запустить на выполнение следующую команду:
dsshost /p:50000 /t:50001 /m:"<путь к файлу манифеста сервиса>"
4. Оценка времени выполнения
Время выполнения вычислений будем определять с помощью класса Stopwatch:
Stopwatch sWatch = new Stopwatch(); sWatch.Start(); <выполняемый код> sWatch.Stop(); Console.WriteLine(sWatch.ElapsedMilliseconds.ToString());
5. Реализация вычислений
5.1. Объявление структур данных
Матрицу A, векторы B и C, переменные для хранения их размеров определим глобально:
int[,] A; int[] B; int[] C; int m; int n;
Метод TestFunction() запускает вычисления. Сначала в методе выполняется умножение матрицы на вектор с помощью последовательного алгоритма, затем – та же задача решается с помощью параллельных вычислений. Рассмотрим этот метод.
Выполняется инициализация структур данных:
nc = 2; // количество ядер m = 11000; // количество строк матрицы n = 11000; // количество столбцов матрицы A = new int[m, n]; B = new int[n]; C = new int[m];
Генерируется матрица A и вектор B:
Random r = new Random(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) A[i, j] = r.Next(100); } for (int j = 0; j < n; j++) B[j] = r.Next(100);
5.2. Последовательный алгоритм перемножения
Рассмотрим метод SequentialMul:
Stopwatch sWatch = new Stopwatch(); sWatch.Start(); for (int i = 0; i < m; i++) { C[i] = 0; for (int j = 0; j < n; j++) C[i] += A[i, j] * B[j]; } sWatch.Stop(); Console.WriteLine("Последовательный алгоритм = {0} мс.", sWatch.ElapsedMilliseconds.ToString());
5.3. Параллельный алгоритм перемножения
Параллельная обработка выполняется с помощью запуска нескольких копий вычислительного метода. Каждая копия метода выполняет обработку определённой части исходных данных. Для описания задания для каждого метода используется класс InputData:
public class InputData { public int start; // начало диапазона public int stop; // начало диапазона }
Поля start / stop класса хранят номер начальной / конечной строки вектора C, которые рассчитываются с помощью экземпляра вычислительного метода.
Рассмотрим метод ParallelMul:
// создание массива объектов для хранения параметров InputData[] ClArr = new InputData[nc]; for (int i = 0; i < nc; i++) ClArr[i] = new InputData
Далее, задаются исходные данные для каждого экземпляра вычислительного метода:
// делим количество строк а матрице на nc частей int step = (Int32)(m / nc); // заполняем массив параметров int c = -1; for (int i = 0; i < nc; i++) { ClArr[i].start = c + 1; ClArr[i].stop = c + step; c = c + step; }
Создаётся диспетчер с пулом из двух потоков:
Dispatcher d = new Dispatcher(nc, "Test Pool"); DispatcherQueue dq = new DispatcherQueue("Test Queue", d);
Описывается порт, в который каждый экземпляр метода Mul() отправляет сообщение после завершения вычислений:
Port<int> p = new Port<int>();
Метод Arbiter.Activate помещает в очередь диспетчера две задачи (два экземпляра метода Mul):
for (int i = 0; i < nc; i++) Arbiter.Activate(dq, new Task<InputData, Port<int>>(ClArr[i], p, Mul));
Первый параметр метода Arbiter.Activate – очередь диспетчера, который будет управлять выполнением задачи, второй параметр – запускаемая задача.
С помощью метода Arbiter.MultipleItemReceive запускается задача (приёмник), которая обрабатывает получение двух сообщений портом p:
Arbiter.Activate(Environment.TaskQueue, Arbiter.MultipleItemReceive(true, p, nc, delegate(int[] array) { Console.WriteLine("Вычисления завершены"); }));
Приёмник используется для определения момента окончания вычислений. Он сработает, только после того, как в порт p придёт два сообщения. В делегат, описанный в приёмнике, можно включить действия, которые должны быть выполнены после завершения процесса умножения.
Метод Mul() выполняет перемножение части матрицы на вектор:
void Mul(InputData data, Port<int> resp) { Stopwatch sWatch = new Stopwatch(); sWatch.Start(); for (int i = data.start; i < data.stop; i++) { C[i] = 0; for (int j = 0; j < n; j++) C[i] += A[i, j] * B[j]; } sWatch.Stop(); Console.WriteLine("Поток № {0}: Параллельный алгоритм = {1} мс.", Thread.CurrentThread.ManagedThreadId, sWatch.ElapsedMilliseconds.ToString()); resp.Post(1); }
Метод Mul() имеет два параметра:
1) индекс, хранящий значение элемента массива, который определяет параметры, передаваемые на вход метода;
2) порт завершения, в который отправляется целое число после завершения вычислений.
После завершения вычислений метод Mul отправляет в порт p целое значение (отправляемое значение может быть любым).
Результат вычислений показан на рис. 3.
Рис. 3. Результаты вычислений
ссылка на оригинал статьи http://habrahabr.ru/post/204466/
Добавить комментарий