20090721 hpc exercise2
TRANSCRIPT
Реализация параллельного алгоритма с использованием MPI
Содержание
• Основы MPI• Настройка проекта Visual Studio 2008 • Основные функции MPI• Блокирующие функции обмена
сообщениями. Типы данных• Выполнение упражнения
Стандарт MPI
Стандарт MPI
Что включено в стандарт MPI?
– Парные обмены (point-to-point communications)– Коллективные операции (collective operations)– Операции над группами процессов (process groups)– Операции над коммуникационными контекстами
(communication contexts)– Топологии процессов (process topologies)– Привязки к языкам C и Fortran 77 (language binding)
Термины MPI
Термины MPI
Локальная операция – процедура, не требующая взаимодействия с другими процессами.
Нелокальная операция – процедура, выполнение которой может потребовать выполнения действий на других процессах. Такая процедура МОЖЕТ потребовать взаимодействия с другими процессами.
Коллективная операция – процедура, которая должная быть выполнена всеми процессами группы.
Термины MPI
Блокирующая операция – подразумевает выход из нее только после полного окончания операции, т.е. вызывающий процесс блокируется, пока операция не будет завершена
Неблокирующая операция - подразумевает совмещение операций обмена с другими операциями.Как правило, ее выполнение происходит в параллельном потоке и не вызывающий процесс не блокируется.
Термины MPI
Режимы выполнения
С блокировкой Без блокировки
Стандартная посылка
MPI_Send MPI_Isend
Синхронная посылка
MPI_Ssend MPI_Issend
Буферизованная посылка
MPI_Bsend MPI_Ibsend
Согласованная посылка
MPI_Rsend MPI_Irsend
Прием информации
MPI_Recv MPI_Irecv
Термины MPIКоммуникатор – контекст взаимодействия процессов с помощью
функций MPI. За коммуникатором закрепляется некоторая группа процессов. Также возможно определение виртуальных топологий для упрощения структуризации обменов в рамках коммуникатора. Процессы в группе (и, как следствие, коммуникаторе) нумеруются от 0 до (<размер_группы> - 1)
MPI_COMM_WORLD – предопределенная в библиотеке MPI константа, представляющая коммуникатор, включающий все процессы запущенной MPI-программы.
MPI_COMM_SELF – предопределенная в библиотеке MPI константа, представляющая коммуникатор, включающий только текущий процесс.
Термины MPIС:
– все функции начинаются с префикса MPI_
• int MPI_Init(int *argc, char **argv);• int MPI_Finalize(void);• int MPI_Initialized(int *flag);
– все функции имеют тип int и возвращают либо значение MPI_SUCCESS (в случае нормального завершения), либо код, соответствующий произошедшей ошибке
Термины MPIПри описании функций и их параметров будем
использовать следующие обозначения:
– Данные, помеченные [IN], считаются входными для функции (процедуры), то есть, используются внутри нее, но не изменяются.
– Данные, помеченные [OUT], считаются выходными и могут быть изменены.
– Данные, помеченные [INOUT], используются внутри функции и могут быть изменены.
Структура MPI-программы
Структура MPI-программы
Основные этапы жизненного цикла MPI-программы:
• Запуск• Инициализация среды выполнения MPI• Вычисления• Завершение среды выполнения MPI• Выгрузка
Структура MPI-программы
MPI_Init(argc, argv)– [INOUT] argc – количество параметров командной строки запуска
программы– [INOUT] argv – параметры командной строки, используемые для
передачи параметров в программу
C: int MPI_Init(int *argc, char ***argv)
• Функция MPI_Init создает группу процессов, в которой содержатся все процессы MPI-программы, и соответствующий этой группе коммуникатор MPI_COMM_WORLD.
• Может быть вызвана только один раз, попытка повторной инициализации завершится ошибкой.
Структура MPI-программы
MPI_Finalize()– Без параметров
• C: int MPI_Finalize(void)
Функция MPI_Finalize освобождает ресурсы, занятые средой выполнения MPI.
Структура MPI-программы
#include <stdio.h>#include <mpi.h>
int main(int argc, char* argv[]){
int ierr;
ierr = MPI_Init(&argc, &argv); // Инициализация среды MPI
if (ierr != MPI_SUCCESS)return ierr;
printf("Hello world\n"); // Полезная работа
MPI_Finalize(); // Завершение среды MPI}
Основные функции MPI
Основные функции MPI
MPI_Initialized(flag)– [OUT] flag – возвращает 0, если среда выполнения MPI
не была еще инициализирована
C: int MPI_Initialized(int *flag)
Функция MPI_Initialized позволяет выяснить, была ли среда выполнения MPI уже инициализирована.
Основные функции MPI
MPI_Comm_size(comm, size)– [IN] comm – коммуникатор, размер которого хотим определить.– [OUT] size – количество процессов в коммуникаторе comm
C: int MPI_Comm_size(MPI_Comm comm, int *size)
Функция MPI_Comm_size возвращает в переменную size количество процессов в коммуникаторе comm.
Основные функции MPI
MPI_Comm_rank(comm, rank)– [IN] comm – коммуникатор, относительно которого мы хотим
определить ранг текущего процесса.– [OUT] rank – ранг текущего процесса в коммуникаторе comm
C: int MPI_Comm_rank(MPI_Comm comm, int *rank)
• Функция MPI_Comm_rank возвращает в переменную rank ранг текущего процесса в коммуникаторе comm.
• В различных коммуникаторах ранги одного и того же процесса могут быть различны, но обязательно уникальны в рамках каждого коммуникатора.
Блокирующие функции обмена сообщениями
Блокирующие функции обмена сообщениями
• Сообщения – данные, передаваемые между процессами, вместе с дополнительной информацией – их описанием
• Сообщения помечаются тегом – произвольно задаваемым программистом целым числом
• Тег и дополнительная информация передаются в оболочке сообщения
• Каждой операции отправки сообщения должна соответствовать операция приема.
Отправка сообщения: - оболочка - данные
Прием сообщения: - сравнить оболочки - принять данные=?
Блокирующие функции обмена сообщениями
Оболочка сообщения содержит:– Ранг процесса-источника сообщения– Ранг процесса-получателя сообщения– Количество передаваемых в сообщении данных– Тег сообщения– Коммуникатор, в рамках которого происходит
отправка
Отправка сообщения: - оболочка - данные
Прием сообщения: - сравнить оболочки - принять данные=?
Блокирующие функции обмена сообщениями
MPI_Send(buf, count, datatype, dest, tag, comm)– [IN] buf – указатель на буфер отправляемых данных– [IN] count – количество данных типа datatype в буфере buf– [IN] datatype – тип отправляемых данных– [IN] dest – ранг процесса, которому отсылаем данные– [IN] tag – тэг отсылаемых данных– [IN] comm – коммуникатор
• С: MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, • int tag, MPI_Comm comm)
Функция MPI_Send отсылает сообщение из буфера buf процессу с рангом dest.
Блокирующие функции обмена сообщениями
MPI_Recv(buf, count, datatype, source, tag, comm, status)– [OUT] buf – указатель на буфер, в который будут сохранены полученные данные – [IN] count – количество принимаемых данных типа datatype– [IN] datatype – тип принимаемых данных– [IN] source – ранг процесса, от которого принимаем данные, или MPI_ANY_SOURCE– [IN] tag – тэг принимаемых данных или MPI_ANY_TAG– [IN] comm – коммуникатор– [OUT] status – статус полученного сообщения
• С: MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
Функция MPI_Recv принимает сообщение от процесса с рангом source. В случае, если ранг не известен или не важен, можно указать значение MPI_ANY_SOURCE. Если неизвестен или неважен тэг сообщения, можно указать значение MPI_ANY_TAG.
Типы данныхMPI C
MPI_CHAR signed charMPI_SHORT signed short intMPI_INT signed intMPI_LONG signed long intMPI_UNSIGNED_CHAR unsigned charMPI_UNSIGNED_SHORT unsigned short intMPI_UNSIGNED unsigned intMPI_UNSIGNED_LONG unsigned long intMPI_FLOAT floatMPI_DOUBLE doubleMPI_LONG_DOUBLE long double
Настройка проекта в Visual Studio 2008
Настройка проекта в Visual Studio 2008
1) Project -> Project Properties-> C\C++-> General –> Additional Include Directories :
• затем нажмите на икону и выберите папку: C:\Program Files\Microsoft HPC Pack 2008 SDK\Include
2) Project -> Project Properties-> Linker -> General -> Additional Library Directories :
• C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\i386 3) Project -> Project Properties-> Linker -> Input: msmpi.lib Ws2_32.lib
• Для платформы x64, и введите папки: • C:\Program Files\Microsoft HPC Pack 2008 SDK\Include • C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\amd64
Настройка проекта в Visual Studio 2008
Настройка проекта в Visual Studio 2008
Настройка проекта в Visual Studio 2008
Настройка проекта в Visual Studio 2008•
Выполнение упражнения
Выполнение упражнения
MasterMaster
WorkerWorker WorkerWorkerMasterMaster
MasterMaster
Send / Recv
WorkerWorker
Выполнение упражения1. Запустите проект в папке Exercises\03 MPI\MPIContrastStretch. Настройте проект для создания
MPI приложений как сказано выше. Выберите архитектуру процессора (Win32 или x64). Добавьте в файл “app.h” строку #include <mpi.h>. В самом начале главной функции добавьте вызовы функций MPI_Init, MPI_Comm_size, MPI_Comm_rank, и gethostname. Для более простого способа отладки объявите связанные с ними переменные как глобальные. Например :int myRank;int numProcs;char host[256];
int main(int argc, char *argv[]){
MPI_Init(&argc, &argv);MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
MPI_Comm_rank(MPI_COMM_WORLD, &myRank); gethostname(host, sizeof(host)/sizeof(host[0]));
…MPI_Finalize();return 0;
}
Выполнение упражнения 2. Cоздайте тип данных MPI_PIXEL_T ,добавив вызов функции CreateMPIPixelDatatype
после вызова MPI_Init. Новый тип MPI данных назовем MPI_PIXEL_T:
…gethostname(host,sizeof(host)/sizeof(host[0]));MPI_Datatype MPI_PIXEL_T = CreateMPIPixelDatatype();
• Для его уничтожения необходимо перед вызовом функции MPI_Finalize все процессы должны вызвать функцию MPI_Type_free:
…MPI_Type_free(&MPI_PIXEL_T);MPI_Finalize();return 0;
Выполнение упражнения3. Найдите в файле место, где происходит вызов ContrastStretch и закомментируйте его.
добавьте ниже:
PIXEL_T **chunk = NULL;int myrows = 0;int mycols = 0;
// разделение матрицы на частиchunk = DistributeImage(image, rows, cols, myrows, mycols, MPI_PIXEL_T); assert(chunk != NULL); assert(rows > 0); assert(cols > 0);assert(myrows > 0); assert(mycols > 0);// chunk = ContrastStretch(chunk, myrows, mycols, steps, stepby, MPI_PIXEL_T); // собирание матрицыimage = CollectImage(image, rows, cols, chunk, myrows, mycols, MPI_PIXEL_T);
Выполнение упражнения5. Добавьте в проект файлы Distribute.cpp и Collect.cpp. Добавьте в файл Distribute.cpp
функцию DistributeImage для распределения частей матрицы между вычислительными узлами :#include "app.h”#include "mpi.h"PIXEL_T **DistributeImage(PIXEL_T **image, int &rows, int &cols, int &myrows, int &mycols, MPI_Datatype MPI_PIXEL_T){
return NULL;}
• добавьте в файл Collect.cpp функцию CollectImage для сбора частей матрицы в единую матрицу. :#include "app.h”#include "mpi.h"PIXEL_T **CollectImage(PIXEL_T **image, int rows, int cols, PIXEL_T **chunk, int myrows, int mycols, MPI_Datatype MPI_PIXEL_T){
return NULL;}
Выполнение упражнения
• Добавьте в файл “app.h” определения функций:
PIXEL_T **DistributeImage(PIXEL_T **image, int &rows, int &cols, int &myrows, int &mycols, MPI_Datatype MPI_PIXEL_T);PIXEL_T **CollectImage(PIXEL_T **image, int rows, int cols, PIXEL_T **chunk, int myrows, int mycols, MPI_Datatype MPI_PIXEL_T);
• Также в файле “app.h” необходимо объявить внешними (external) переменные:
extern int myRank;extern int numProcs;extern char host[256];
Выполнение упражения6. В файле Main.cpp добавим код, для того чтобы ввод и вывод файлов осуществлялся только главным
узлом. Находим строку где выводится "** Reading bitmap from '" и добавляем выше и ниже:
double time = 0.0;
if (myRank == 0){
cout << "** Reading bitmap from '" << infile << "'..." << endl;image = ReadBitmapFile(infile, bitmapFileHeader, bitmapInfoHeader, rows, cols);if (image == NULL){
cout << endl;cout << "** Failed to open image file, halting..." << endl; MPI_Abort(MPI_COMM_WORLD, 1);
}cout << "** Bitmap size is " << rows << " rows, " << cols << " cols, " << rows*cols << " pixels..." << endl;
cout << endl; startTime = clock();
}
//image = ContrastStretch(image, rows, cols, steps, stepby);
Выполнение упражнения7. Изменим функцию DistributeImage:PIXEL_T **chunk = NULL; int tag = 0;int params[2] = {0, 0};cout << myRank << " (" << host << "): Distributing image..." << endl;if (myRank == 0) // выполняется главным узлом:{int rowsPerProc = rows / numProcs;int leftOverRows = rows % numProcs;params[0] = rows;params[1] = cols; for (int dest=1; dest < numProcs; dest++) // послание каждому процессу размера матрицы MPI_Send(params, sizeof(params)/sizeof(params[0]), MPI_INT, dest, tag, MPI_COMM_WORLD); for (int dest=1; dest < numProcs; dest++) // послание части матрицы MPI_Send(image[leftOverRows + dest*rowsPerProc], rowsPerProc*cols, MPI_PIXEL_T, dest, tag,
MPI_COMM_WORLD);myrows = rowsPerProc + leftOverRows;mycols = cols;chunk = New2dMatrix<PIXEL_T>(myrows+2, mycols); // почему на две строки
// больше? Смотри функцию изменения контрастностиmemcpy_s(chunk[1], myrows*mycols*sizeof(PIXEL_T), image[0], myrows*mycols*sizeof(PIXEL_T));
}•
Выполнение упражненияelse //выполняется вычислительными узлами{MPI_Status status;MPI_Recv(params, sizeof(params)/sizeof(params[0]), MPI_INT, 0 /*master*/, tag, MPI_COMM_WORLD,
&status);rows = params[0];cols = params[1];myrows = rows / numProcs; // размер части матрицы
mycols = cols;
chunk = New2dMatrix<PIXEL_T>(myrows+2, mycols); // почему на две строки // больше? Смотри функцию изменения контрастности
MPI_Recv(chunk[1], myrows*mycols, MPI_PIXEL_T, 0 /*master*/, tag, MPI_COMM_WORLD, &status);}
return chunk;
Выполнение упражнения8. Изменим функцию CollectImage :
assert(chunk != NULL); assert(rows > 0); assert(cols > 0); assert(myrows > 0); assert(mycols > 0);
int tag = 0;
cout << myRank << " (" << host << "): Collecting image..." << endl;if (myRank > 0) // вычислительные узлы{
int dest = 0; // to masterMPI_Send(chunk[1], myrows*mycols, MPI_PIXEL_T, dest, tag,
MPI_COMM_WORLD);}
else // главный вычислительный узел{
assert(image != NULL); MPI_Status status;
memcpy_s(image[0], myrows*mycols*sizeof(PIXEL_T), chunk[1], myrows*mycols*sizeof(PIXEL_T));
int rowsPerProc = rows / numProcs; int leftOverRows = rows % numProcs;
// получение данных от узлов for (int src=1; src < numProcs; src++)
MPI_Recv(image[leftOverRows + src*rowsPerProc], rowsPerProc*cols, MPI_PIXEL_T, src, tag, MPI_COMM_WORLD, &status);
}Delete2dMatrix<PIXEL_T>(chunk);
return image;
Выполнение упражнения Скомпилируйте приложение, скопируйте изображение в папку с EXE файлом и
запустите приложение. С помощью программы WinDiff сравните полученные изображения.
9. В файле “Main. cpp” уберите комментарии для строки chunk = ContrastStretch(chunk, myrows, mycols, steps, stepby, MPI_PIXEL_T);
Измените определение функций ContrastStretch в файл “app.h” :
PIXEL_T **ContrastStretch(PIXEL_T **image, int rows, int cols, int steps, int stepby, MPI_Datatype MPI_PIXEL_T);
Откройте файл ContrastStretch.cpp и измените код в соответствии с
PIXEL_T **ContrastStretch(PIXEL_T **image, int rows, int cols, int steps, int stepby, MPI_Datatype MPI_PIXEL_T)
{cout << myRank << " (" << host << "): Processing " << rows << "rows,"
<< cols << " cols..." << endl;PIXEL_T **image2 = New2dMatrix<PIXEL_T>(rows+2, cols); MPI_Status status; int tag = 0;//Обратите внимание на два дополнительных строки
Выполнение упражнения
Выполнение упражнения int firstRow = 1; // это справедливо для всех узлов кроме главного
int lastRow = rows;
if (myRank == 0) // главный узел начинает вычисления со второй строкиfirstRow = 2;
if (myRank == numProcs-1) // последний узел не вычисляет значения для //последней строки
lastRow = rows-1;
bool converged = false;
Выполнение упражнения10. Для добавления возможности обмена строками необходимо добавить код в самом начале цикла while в файле ContrastStretch.cpp:
cout << myRank << " (" << host << "): ** Step " << step << "..." << endl;
if (myRank < numProcs-1) // все посылают «вниз» последнюю (строку кроме последнего узла): MPI_Send(image[lastRow], cols, MPI_PIXEL_T, myRank+1, tag, MPI_COMM_WORLD);
if (myRank > 0) // все получают строку кроме первого узла MPI_Recv(image[firstRow-1], cols, MPI_PIXEL_T, myRank-1, tag, MPI_COMM_WORLD, &status);if (myRank > 0) // все посылают вверх первую строку (кроме //первого узла):
MPI_Send(image[1], cols, MPI_PIXEL_T, myRank-1, 0 /*tag*/, MPI_COMM_WORLD);if (myRank < numProcs-1) // все получают первую строку (кроме последнего узла
MPI_Recv(image[rows+1], cols, MPI_PIXEL_T, myRank+1, 0 /*tag*/, MPI_COMM_WORLD, &status);
0
1
2
3
Выполнение упражнения
11. Изменение цикла for для вычисления переменной diffs :
if (myRank > 0) // вычислительные узлы:{
MPI_Send(&diffs, 1, MPI_LONG_LONG, 0 /*master*/, 0 /*tag*/, MPI_COMM_WORLD);MPI_Recv(&diffs, 1, MPI_LONG_LONG, 0 /*master*/, 0 /*tag*/, MPI_COMM_WORLD, &status);
}
Выполнение упражненияelse // главный узел:{long long temp;for (int src=1; src < numProcs; src++) // получение значений от все вычислительных
//узлов:{MPI_Recv(&temp, 1, MPI_LONG_LONG, MPI_ANY_SOURCE, 0 /*tag*/,
MPI_COMM_WORLD, &status);diffs += temp; // суммирование}
for (int dest=1; dest < numProcs; dest++) // отправка вычислительным узлам //нового значения
MPI_Send(&diffs, 1, MPI_LONG_LONG, dest, 0 /*tag*/, MPI_COMM_WORLD);}
cout << " (diffs until convergence: " << diffs << ")" << endl;
Выполнение упражнения12. Обновление циклов копирования матрицы:
for (int row = firstRow; row <= lastRow; row++)for (int col = 1; col < cols-1; col++)
image[row][col] = image2[row][col];
13.И в цикле while diffs = 0;
for (int row = firstRow; row < lastRow; row++){
for (int col = 1; col < cols-1; col++){
14.Скомпилируйте проект и запустите локальной машинеmpiexec –n 4 MPIApp.exe
14. Запишите полученные значения.
Выполнение упражнения
Выполнение упражнения1. Скомпилируйте приложение для выполнения на процессоре x64 и
скопируйте его вместе с изображением в своем папку на кластере2. Для выполнения MPI-приложения на кластере запустите Job Manager,
выберите адрес кластера: HN.PRACTICUM ( 193.232.2.150, <username>: <userpassword>,CLUSTER)
3. Добавьте новое задание, указав в командной строкеmpiexec.exe MPIContrastStretch.exe Sunset.bmp out.bmp 10 10
1. Добавьте в качестве настроек:– \\hn\apps\<username>– \\hn\apps\<username>\out.txt– \\hn\apps\<username>\err.txt
Выполнение упражнения
Заключение
© 2008 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries. The information herein is for informational purposes only and represents the current view of Microsoft Corporation as
of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation.
MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.