Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

u_course

.pdf
Скачиваний:
39
Добавлен:
04.06.2015
Размер:
1.87 Mб
Скачать

Средства разработки параллельных программм

201

принять сообщение, отправленное обычной операцией. Буферы приема и посылки обязательно должны быть различными. Функция имеет следующие параметры: sbuf – адрес начала буфера посылки сообщения; scount – число передаваемых элементов в сообщении; stype – тип передаваемых элементов; dest

– номер процесса-получателя; stag – идентификатор посылаемого сообщения; rbuf – адрес начала буфера приема сообщения (выходной параметр); rcount – число принимаемых элементов сообщения; rtype – тип принимаемых элементов; source – номер процесса-отправителя; rtag – идентификатор принимаемого сообщения; comm – коммуникатор; status – параметры принятого сообщения (выходной параметр).

ФУНКЦИИКОЛЛЕКТИВНОГОВЗАИМОДЕЙСТВИЯПРОЦЕССОВ

Основные особенности, связанные с использованием функций коллективного взаимодействия процессов, обсуждались в главе 6 (см., в частности, рис. 6.1 – 6.2). Ниже приведен синтаксис использования этих функций с привязкой к языку C.

Отметим, что в процессе коллективного взаимодействия участвуют все процессы приложения, т.е. соответствующая процедура должна быть вызвана каждым процессом, быть может, со своим набором параметров. Возврат из процедуры коллективного взаимодействия может произойти в тот момент, когда участие процесса в данной операции уже закончено. Как и для блокирующих процедур, возврат означает то, что разрешен свободный доступ к буферу приема или посылки, но не означает ни того, что операция завершена другими процессами, ни даже того, что она ими начата (если это возможно по смыслу операции).

Синхронизация барьером

int MPI_Barrier(MPI_Comm comm);

Функция синхронизации процессов, блокирует работу процессов, вызвавших данную функцию, до тех пор, пока все оставшиеся процессы группы, ассоциированной с коммуникатором comm, также не выполнят эту функцию.

Глобальные функции связи

int MPI_Bcast (void *buf, int count, MPI_Datatype datatype,

int source, MPI_Comm comm);

Функция осуществляет рассылку сообщения, содержащегося в buf с count элементами типа datatype от процесса source всем процессам, включая рассылающий процесс. При возврате из функции содержимое буфера buf процесса source будет скопировано в локальный буфер процесса. Значения

Средства разработки параллельных программм

202

параметров count, datatype и source должны быть одинаковыми у всех процессов.

int MPI_Gather(void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype,

int dest, MPI_Comm comm);

Функция осуществляет сбор данных со всех процессов, ассоциированных с коммуникатором comm, в буфере rbuf процесса dest. Каждый процесс, включая dest, посылает содержимое своего буфера sbuf процессу dest. Собирающий процесс сохраняет данные в буфере rbuf, располагая их в порядке возрастания номеров процессов. Параметр rbuf имеет значение только на собирающем процессе и на остальных игнорируется, значения параметров scount, rcount, stype, rtype и dest должны быть одинаковыми у всех процессов. Существует расширяющая функциональные возможности функция MPI_Gatherv(), позволяющая использовать различное количество данных из каждого процесса, поскольку параметр rcount является массивом.

int MPI_Scatter(void* sbuf, int scount, MPI_Datatype stype, void* rbuf, int rcount,

MPI_Datatype rtype, int root, MPI_Comm comm);

Функция является обратной к MPI_Gather(). Результат выглядит так, как будто корень выполнил n посылающих операций MPI_Send(...) всем процессам, ассоциируемым с коммуникатором comm, включая самого себя, а каждый принимающий процесс выполнил функцию MPI_Recv(...) от корневого процесса. Аргументы scount и stype в корне должны быть равны аргументам rcount и rtype во всех процессах. Это подразумевает, что количество посланных данных должно быть равно количеству полученных данных, попарно между каждым процессом и корнем. Все аргументы в функции значимы на корневом процессе, в то время как на других процессах, значимы только аргументы rbuf, rcount, rtype, root, comm. Посылающийся буфер игнорируется для всех процессов, не принадлежащих корню. Существует расширяющая функциональные возможности функция MPI_Scatterv(), позволяющая посылать различное количество данных каждому процессу, поскольку параметр scount является массивом.

int MPI_Allgather(void* sbuf, int scount, MPI_Datatype stype,

void* rbuf, int rcount, MPI_Datatype rtype, MPI_Comm comm);

Функция аналогична MPI_ Gather() за исключением того, что все процессы, ассоциированные с коммуникатором comm, получают результат от всех процессов, а не только от одного корня, то есть j-й блок данных, посланных из каждого процесса, получен каждым процессом и размещается в j-м блоке буфера rbuf. Аргументы scount и stype в процессе должны быть равны во всех процессах. Результат выполнения функции выглядит так, как будто все процессы выполнили n запросов к MPI_Gather (...) для root = 0, ..., n-l. Правила ис-

Средства разработки параллельных программм

203

пользования MPI_Allgather() аналогичны соответствующим правилам для MPI_Gather(). Существует расширяющая функциональные возможности функция MPI_Allgatherv(), аналогичная MPI_ Gatherv() за исключением того, что все процессы получают результат от всех процессов. При этом позволяется используются буферы различного размера.

int MPI_Alltoall(void* sbuf, int scount, MPI_Datatype stype,

void* rbuf, int rcount, MPI_Datatype rtype, MPI_Comm comm);

Функция расширяет возможности MPI_Allgather() для случая, когда каждый процесс, ассоциированный с коммуникатором comm, посылает различные данные на каждый из приемников, j-й блок, посланный из процесса i, получен процессом j и размещен в i-м блоке буфера rbuf. Аргументы scount и stype в процессе должны быть равны аргументам rcount и rtype в любом другом процессе. Это подразумевает, что количество посланных данных должно быть равно количеству полученных, попарно между каждой парой процессов. Все аргументы во всех процессах значимы. Существует расширяющая функциональные возможности функция MPI_Alltoallv().

Глобальные функции приведения

int MPI_AllReduce(void *sbuf, void *rbuf, int count, MPI_Datatype datatype, MPI_Op op,

MPI_Comm comm);

Функция осуществляет выполнение count глобальных операций op с возвратом count результатов во всех процессах, ассоциированных с коммуникатором comm, в буфере rbuf. Операция выполняется независимо над соответствующими аргументами всех процессов. Значения параметров count и datatype у всех процессов должны быть одинаковыми. Из соображений эффективности реализации предполагается, что операция op обладает свойствами ассоциативности и коммутативности. Список допустимых операций приведен в табл. 7.1.

int MPI_Reduce(void *sbuf, void *rbuf, int count, MPI_Datatype datatype,

MPI_Op op, int root, MPI_Comm comm);

Функция аналогична предыдущей, но результат будет записан в буфер rbuf только у процесса root.

int MPI_Reduce_scatter(void* sbuf, void* rbuf, int *rcounts,

MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

Функция предполагает сначала выполнение поэлементной редукции на векторе из count = i rcounts [i] элементов в буфере посылки, определенном sendbuf, count и datatype. Далее полученный вектор результатов разделяется на n непересекающихся сегментов, где n - число членов в группе. Сегмент i со-

Средства разработки параллельных программм

204

держит rcount[i] элементов, при этом i-й сегмент посылается i-му процессу и хранится в буфере приема, определяемом rbuf, rcounts[i] и datatype.

Таблица 7.1 Операции, предопределенные для глобальных функций приведения

Имена операций

Значения операций

MPI_MAX

максимум

MPI_MIN

минимум

MPI_SUM

сумма

MPI_PROD

произведение

MPI_LAND

логическое "И"

MPI_BAND

поразрядное "И"

MPI_L0R

логическое "ИЛИ"

MPI_B0R

поразрядное "ИЛИ"

MPI_XOR

логический "XOR"

MPI_BXOR

поразрядный "XOR"

MPI_MAXLOC

максимальное значение и локализация

MPI_MINLOC

значение минимума и локализация

int MPI_Scan(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype,

MPI_Op op, MPI_Comm comm);

Функция выполняет префиксную редукцию данных, распределенных в группе. Операция возвращает в приемный буфер процесса i редукцию значений в посылающих буферах процессов с номерами 0, ..., i (включительно). Тип поддерживаемых операций, их семантика, и ограничения на буфера посылки и приема - такие же, как и для MPI_Reduce().

Пример: Умножение матрицы на вектор

Для демонстрации работы коллективных обменов приведем код программы, реализующий алгоритм умножения прямоугольной матрицы на вектор. Размерности матрицы и вектора, как и число процессоров заранее неизвестны. Данные разрезыются на size линий, где size – количество процессов. Количество строк в линии определяется в зависимости от количества строк в матрице, которое в свою очередь не обязательно кратно size.

#include<mpi.h> #include <stdio.h> #include<stdlib.h>

int main(int argc, char** argv)

{

char fileO[10]="in.txt"; //файл с исходными данными FILE *f_in;

int i,j;

int rows,cols; // колочество строк и столбцов матрицы

Средства разработки параллельных программм

205

int C1,C2; int rank,size;

int *matrix,*vector,*result,*matrix1,*rez;

int *counts,*countsrez; // количество элементов отсылаемое каждому процессору int *disp,*disprez; // смещение относительно начала массива

// для функций MPI_Scatterv() и MPI_Gatherv()

MPI_Status status; MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD,&rank);

MPI_Comm_size(MPI_COMM_WORLD,&size);

// данные считывает 0-ой процессор if(!rank)

{

if ((f_in = fopen(fileO, "r")) == NULL)

printf ("\n Cannot open %s file.\n",fileO);

fscanf(f_in,"%d",&rows); //считываем количество строк fscanf(f_in,"%d",&cols); //считываем количество столбцов

matrix=(int*)malloc(sizeof(int)*cols*rows); //выделяем память для хранения матрицы vector=(int*)malloc(sizeof(int)*cols); //выделяем память для хранения вектора

for(i=0;i<cols*rows;i++)

fscanf(f_in,"%d",matrix+i); //считываем матрицу из файла

for(i=0;i<cols;i++)

fscanf(f_in,"%d",vector+i); //считываем вектор из файла

result=(int*)malloc(sizeof(int)*rows); //выделяем память для хранения результата

}

//Рассылаем rows, cols и vector всем процессорам

MPI_Bcast(&rows,1,MPI_INT,0,MPI_COMM_WORLD);

MPI_Bcast(&cols,1,MPI_INT,0,MPI_COMM_WORLD);

//Прежде чем получить вектор все, кроме 0-го процесса

//должны выделить память для него

if (rank) vector=(int*)malloc(sizeof(int)*cols); MPI_Bcast(vector,cols,MPI_INT,0,MPI_COMM_WORLD);

//Выделяем память для вычислений counts=(int*)malloc(sizeof(int)*size); disp=(int*)malloc(sizeof(int)*size); countsrez=(int*)malloc(sizeof(int)*size); disprez=(int*)malloc(sizeof(int)*size); matrix1=(int*)malloc(sizeof(int)*cols*rows); rez=(int*)malloc(sizeof(int)*rows);

//вычисляем С1 и С2

C1=rows/size;

C2=rows%size;

//Данные разрезыются на size линий. Первые size-C2 линий имеют C1 строк // а остальные C2 линий имеют C1+1 строку

//Для <size-C2

for (i=0;i<size-C2;i++)

{

Средства разработки параллельных программм

206

counts[i]=C1*cols; //количество посылаемых элементов i-му процессору countsrez[i]=C1; //количество посылаемых элементов i-м процессором нулевому

disprez[i]=i*C1; //смещение элементов в итоговом массиве disp[i]=i*C1*cols; //смещение элементов в исходном массиве

}

//Для size-C2 counts[size-C2]=(C1+1)*cols; countsrez[size-C2]=C1+1;

disp[size-C2]=(size-C2)*C1*cols; disprez[size-C2]=(size-C2)*C1;

//Для >size-C2

for (i=size-C2+1;i<size;i++)

{

counts[i]=(C1+1)*cols;

countsrez[i]=C1+1;

disp[i]=(size-C2)*C1*cols+(i-(size-C2))*(C1+1)*cols; disprez[i]=(size-C2)*C1+(i-(size-C2))*(C1+1);

}

//Рассылаем строки матрицы по процессорам

MPI_Scatterv(matrix,counts,disp,MPI_INT, matrix1,counts[rank],MPI_INT,0,MPI_COMM_WORLD);

for(i=0;i<counts[rank]/cols;i++) rez[i]=0; //Обнуляем rez[]

//Вычисляем значение элемента результирующего вектора for(i=0;i<counts[rank]/cols;i++)

for(j=0;j<cols;j++)

rez[i]=matrix1[i*cols+j]*vector[j]+rez[i];

printf("====================\n");

//Собираем результат в 0-процессоре

MPI_Gatherv(rez, countsrez[rank], MPI_INT,result, countsrez, disprez, MPI_INT, 0, MPI_COMM_WORLD);

//Контрольная печать исходных данных и результатов 0-вым процессором if(!rank)

{

printf("\nMatrix\n");

for(i=0;i<rows;i++)

{

for(j=0;j<cols;j++)

printf("%d ",matrix[i*cols+j]); //Исходная матрица printf("\n");

}

printf("====================\n");

printf("\nVector\n");

for(j=0;j<cols;j++)

printf("%d ",vector[j]); //Исходный вектор printf("\n");

Средства разработки параллельных программм

207

printf("====================\n");

printf("\nResult\n");

printf("====================\n");

for(i=0;i<rows;i++)

printf("%d ",result[i]); //Полученный вектор-результат printf("\n");

}

MPI_Finalize(); return 0;

}

Предопределенные константы и типы данных

В стандарте MPI существует несколько предопределенных типов, среди них:

MPI_Status – структура, содержащая в частности следующие атрибу-

ты сообщений:

MPI_Source – номер процесса отправителя;

MPI_Tag – идентификатор сообщения, целое неотрицательное число, лежащее в диапазоне от 0 до 32767;

MPI_Error – код ошибки;

MPI_Request – системный тип; идентификатор операции посыл-

ки/приема сообщения;

MPI_Comm – системный тип; идентификатор коммуникатора;

MPI_COMM_WORLD – зарезервированный идентификатор группы, со-

стоящей их всех процессов приложения.

Предопределенные константы типа элементов сообщений используются для указания типа пересылаемых данных в функциях приема/передачи представлены в табл. 7.2.

Таблица 7.2

Предопределенные константы типов в MPI

Константы MPI

Тип в C

MPI_CHAR

signed char

MPI_SHORT

signed int

MPI_INT

signed int

MPI_LONG

signed long int

MPI_UNSIGNED_CHAR

unsigned char

MPI_UNSIGNED_SHORT

unsigned int

MPI_UNSIGNED

unsigned int

MPI_UNSIGNED_LONG

unsigned long int

MPI_FLOAT

float

MPI_DOUBLE

double

MPI_LONG_DOUBLE

long double

Средства разработки параллельных программм

208

Поскольку сообщение может быть принято с аргументами-джокерами («принимай что угодно» – MPI_ANY_TAG, «от кого угодно» – MPI_ANY_SOURCE), то в структуре status сохраняются реально принятые сведения об источнике и сообщении. Поле структуры status MPI_ERROR, как правило, проверять необязательно – обработчик ошибок, устанавливаемый MPI по умолчанию, в случае сбоя завершит выполнение программы до возврата из MPI_Recv(). Отметим, что структура типа MPI_Status не содержит данные о фактической длине пришедшего сообщения. Длину следует узнавать информационной функцией MPI_Get_count().

Определены константы-пустышки:

MPI_COMM_NULL – неопределенный коммуникатор;

MPI_DATATYPE_NULL – неопределенный тип данных;

MPI_REQUEST_NULL – неопределенный идентификатор операции по-

сылки/приема сообщения.

Константа неопределенного значения, используемая в процедуре

MPI_Comm_Split(), имеет имя MPI_UNDEFINED.

КОНТРОЛЬНЫЕВОПРОСЫ

1.Три основных признака MPI-функций: блокирующие / неблокирующие, локальные, коллективные функции.

2.Дайте определения группы, области связи, коммуникатора.

3.MPI-функции создания коммуникаторов.

4.Обрамляющие и информационные функции. Структура простейшей MPI-программы.

5.Парные функции приема / передачи сообщений. Перечислите 8 основных функций посылки сообщений. В чем особенность каждой из них.

6.Блокирующие операции обмена.

7.MPI-функции для получения информации о сообщении.

8.Неблокирующие операции обмена.

9.Проверка выполнения обмена

10.Передача сообщения в автоматическом режиме (блокирующая / неблокирующая).

11.Передача сообщения с буферизацией (блокирующая / неблокирую-

щая).

12.Синхронная передача сообщения (блокирующая / неблокирующая).

14.Передача сообщения по готовности (блокирующая / неблокирую-

щая).

Средства разработки параллельных программм

209

15.Реализация отложенных обменов.

16.Совмещение операций посылки и передачи.

17.Функции коллективного взаимодействия процессов: синхронизация барьером.

18.Функции коллективного взаимодействия процессов: глобальные функции связи

19.Функции коллективного взаимодействия процессов: глобальные функции приведения

20.Предопределенные константы и типы данных в MPI.

СПИСОКЛИТЕРАТУРЫ

1.Бекон, Д. Операционные системы / Бекон Д., Харрис Т.; пер. с англ. – Спб.: Питер; Киев: Издательская группа BHV, 2004 – 800 с.

2.Вальковский, В.А. Распараллеливание алгоритмов и программ. Структурный подход / Вальковский В.А. – М.: Радио и связь, 1989. – 176 с.

3.Воеводин, В.В. Параллельные вычисления / Воеводин В.В., Воеводин Вл.В. – СПб.: БХВ-Петербург, 2002. – 608 с.

4.Гергель, В.П. Основы параллельных вычислений для многопроцессорных вычислительных систем. Учебное пособие / Гергель В.П., Стронгин Р.Г. – Нижний Новгород: Изд-во ННГУ, 2003. – 184 с.

5.Кейслер, С. Проектирование операционных систем для малых ЭВМ.

/С. Кейслер; пер. с англ. – М.: Мир, 1986. – 680 с.

6.Корнеев, В.Д. Параллельное программирование в MPI / Корнеев В.Д.

Новосибирск: Изд-во СО РАН, 2000. – 213 с.

7.Немнюгин, С.А. Параллельное программирование для многопроцессорных вычислительных систем / Немнюгин С.А., Стесик О.Л. – СПб.: БХВ-

Петербург, 2002. – 400с.

8.Ортега, Дж. Введение в параллельнные и векторные методы решения линейных систем / Ортега Дж.; пер. с англ. – М.: Мир, 1991. – 367 с.

9.Программа дисциплины "Параллельные системы и параллельные вычисления"

10.Рихтер, Дж. Windows для профессионалов: создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows / Рихтер Дж.; пер. с англ. – СПб.: «Русская Редакция», 2000. – 752 с.

11.Стивенс, У. Unix: Разработка сетевых приложений / Стивенс У.; пер. с англ. – Спб: Питер, 2004. 1086 с. (Серия «Мастер-класс»).

12.Таненбаум, Э. Распределенные системы. Принципы и парадигмы. Серия «Классика computer science». / Э. Таненбаум, Ван Стеен; пер. с англ. –

СПб.: Питер, 2003. – 877 с.

13.Таненбаум, Э. Современные операционные системы Modern Operating Systems / Э. Таненбаум; пер. с англ. – СПб.: Питер, 2007 г. – 1040 с.

14.Хьюз, Камерон. Параллельное и распределенное программирование на C++ / Хьюз Камерон, Хьюз Трейси; пер. с англ. – М.: Издательский дом

«Вильямс», 2004. – 672 с.

15.Чан, Теренс. Системное программирование на С++ для UNIX. \ Чан Теренс.; пер. с англ. СПб: БХВ-Петербург, 1999 г.

16.Шоу, A. Логическое проектирование операционных систем. / A.

Шоу; пер. с англ. – М.: Мир, 1981. – 360 с.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]