Параллельное программирование с использованием технологии MPI Лекция 8 Томский политехнический университет Аксёнов Сергей Владимирович к.т.н., доцент каф.ОСУ ТПУ Параллельное программирование с использованием технологии MPI Аксёнов С.В. 1 Функция MPI_Reduce() int MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm) Вход Выход Вход sendbuf: recvbuf: count: Вход datatype: Вход Вход Вход op: root: comm: Адрес посылающего буфера Адрес принимающего буфера Количество элементов в посылающем буфере Тип данных элементов посылающего буфера Операция редукции Номер корневого процесса Коммуникатор Функция MPI_Reduce() объединяет элементы входного буфера каждого процесса в группе, используя операцию op, и возвращает объединенное значение в выходной буфер процесса с номером root. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 2 Предопределенные операции редукции Название Значение MPI_MAX Максимум MPI_MIN Минимум MPI_SUM Сумма MPI_PROD Произведение MPI_LAND Логическое И MPI_BAND Поразрядное И MPI_LOR Логическое ИЛИ MPI_BOR Поразрядное ИЛИ MPI_LXOR Логическое исключающее ИЛИ MPI_BXOR Поразрядное исключающее ИЛИ MPI_MAXLOC Максимальное значение и местонахождения MPI_MINLOC Минимальное значение и местонахождения Параллельное программирование с использованием технологии MPI Аксёнов С.В. 3 Пример MPI_Reduce() int main (int argc, char *argv[]) { int *sendbuf, *recvbuf, i, n=5, rank; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); sendbuf = malloc(5*sizeof(int)); recvbuf = malloc(5*sizeof(int)); srand(rank); for (i=0; i<n; i++) { sendbuf[i]=rand()%100; printf("Процесс № %d Значение элемента sendbuf на позиции %d равно %d \n“, rank, i, sendbuf[i]); } MPI_Reduce(sendbuf,recvbuf,n,MPI_INT,MPI_MAX,0,MPI_COMM_WORLD); if (rank==0) for (i=0; i<5; i++) printf(“Процесс № %d Максимальное значение элементов sendbuf на позиции %d равно %d \n“, rank, i, recvbuf[i] ); MPI_Finalize(); Параллельное программирование с return 0: использованием технологии MPI Аксёнов 4 } С.В. Функция MPI_Allreduce() int MPI_Allreduce (void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) Вход Выход Вход sendbuf: recvbuf: count: Вход datatype: Вход Вход op: comm: Адрес посылающего буфера Адрес принимающего буфера Количество элементов в посылающем буфере Тип данных элементов посылающего буфера Операция редукции Коммуникатор MPI имеет варианты каждой из операций редукции, где результат возвращается всем процессам группы. MPI требует, чтобы все процессы, участвующие в этих операциях, получили идентичные результаты. Функция MPI_Allreduce() отличается от MPI_Reduce() тем, что результат появляется в принимающем буфере всех членов группы. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 5 Функция MPI_Reduce_scatter() int MPI_Reduce_scatter(void *sendbuf, void *recvbuf, int *recvcounts, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) Вход Выход Вход sendbuf: recvbuf: recvcounts: Вход datatype: Вход Вход op: comm: Адрес посылающего буфера Адрес принимающего буфера Целочисленный массив, определяющий количество элементов результата, распределенных каждому процессу. Тип данных элементов посылающего буфера Операция редукции Коммуникатор Функция MPI_Reduce_scatter() сначала производит поэлементную редукцию вектора из элементов в посылающем буфере. Далее полученный вектор результатов разделяется на n непересекающихся сегментов, где n – число членов в группе. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 6 Функция MPI_Scan() int MPI_Scan(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) Вход Выход Вход sendbuf: recvbuf: count: Вход datatype: Вход Вход op: comm: Адрес посылающего буфера Адрес принимающего буфера Количество элементов в принимающем буфере Тип данных элементов посылающего буфера Операция редукции Коммуникатор Функция MPI_Scan() используется, чтобы выполнить префиксную редукцию данных, распределенных в группе. Операция возвращает в приемный буфер процесса i редукцию значений в посылающих буферах процессов с номерами 0, ..., i (включительно). Параллельное программирование с использованием технологии MPI Аксёнов С.В. 7 Пользовательские операции приведения int MPI_Op_create(MPI_User_function *function, int commute, MPI_Op *op) Вход Вход function: commute: Выход op: Пользовательская функция Флаг, которому присваивается значение «истина», если операция коммутативна Операция редукции Допускается определение собственных операций приведения. Для этого используется подпрограмма MPI_Op_create (). Описание типа пользовательской функции выглядит следующим образом: typedef void (MPI_User_function) (void *a, void *b, int *len, MPI_Datatype *dtype После завершения операций приведения пользовательская функция должна быть удалена: int MPI_Op_free(MPI_Op *op) Параллельное программирование с использованием технологии MPI Аксёнов С.В. 8 Конструктор MPI_Type_contiguous() int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype &newtype) Вход Вход Вход/Выход count: oldtype: newtype: Число повторений Старый тип Новый тип Функция MPI_Type_contiguous() позволяет копировать тип данных в смежные области. Новый тип newtype есть тип, полученный конкатенацией (сцеплением) count копий старого типа oldtype. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 9 Конструктор MPI_Type_vector() int MPI_Type_vector(int count, int blocklength, MPI_Datatype oldtype, MPI_Datatype &newtype) Вход Вход count: blocklength: Вход stride: Вход Вход/Выход oldtype: newtype: int stride, Число блоков Число элементов в каждом блоке Число элементов между началами каждого блока Старый тип Новый тип Функция MPI_Type_vector() является универсальным конструктором, который позволяет реплицировать типы данных в области, которые состоят из блоков равного объема. Каждый блок получается как конкатенация некоторого количества копий старого типа. Пространство между блоками кратно размеру oldtype. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 10 Конструктор MPI_Type_hvector() int MPI_Type_hvector(int count, int blocklength, MPI_Aint stride, MPI_Datatype oldtype, MPI_Datatype &newtype) Вход Вход count: blocklength: Вход stride: Вход Вход/Выход oldtype: newtype: Число блоков Число элементов в каждом блоке Число байтов между началами каждого блока Старый тип Новый тип Функция MPI_Type_hvector() идентична MPI_Type_vector() за исключением того, что страйд задается в байтах, а не в элементах. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 11 Конструктор MPI_Type_indexed() int MPI_Type_indexed(int count, int *array_of_blocklength, int *array_of_displacement, MPI_Datatype oldtype, MPI_Datatype *newtype) Вход Вход count: array_of_blocklength: Число блоков Число элементов в каждом блоке Вход array_of_displacements : Смещение для каждого блока oldtype: Старый тип Вход/Выход newtype: Новый тип Вход Функция MPI_Type_indexed() позволяет реплицировать старый тип oldtype в последовательность блоков (каждый блок есть конкатенация oldtype), где каждый блок может содержать различное число копий и иметь различное смещение. Все смещения блоков кратны длине старого блока oldtype. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 12 Конструктор MPI_Type_hindexed() int MPI_Type_indexed(int count, int *array_of_blocklength, MPI_Aint *array_of_displacements, MPI_Datatype oldtype, MPI_Datatype *newtype) Вход Вход count: array_of_blocklength: Число блоков Число элементов в каждом блоке Вход array_of_displacements : Смещение для каждого блока oldtype: Старый тип Вход/Выход newtype: Новый тип Вход Функция MPI_Type_hindexed() идентична MPI_Type_indexed() за исключением того, что смещения блоков в массиве array_of_displacements задаются в байтах, а не в кратностях ширины старого типа oldtype. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 13 Конструктор MPI_Type_struct() int MPI_Type_struct(int count, int *array_of_blocklength, MPI_Aint *array_of_displacements, MPI_Datatype *array_of_types, MPI_Datatype *newtype) Вход Вход count: array_of_blocklength: Число блоков Число элементов в каждом блоке Вход array_of_displacements : Смещение для каждого блока Вход array_of_types: Массив типов Вход/Выход newtype: Новый тип Функция MPI_Type_hindexed() идентична MPI_Type_indexed() за исключением того, что смещения блоков в массиве array_of_displacements задаются в байтах, а не в кратностях ширины старого типа oldtype. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 14 Объявление объектов типа данных int MPI_Type_commit(MPI_Datatype datatype) Вход datatype: Объявленный тип данных Объекты типов данных должны быть объявлены перед их использованием в коммуникации. Объявленный тип данных может быть использован как аргумент в конструкторах типов данных. Базисные типы данных объявлять не нужно, поскольку они предопределены. Функция MPI_Type_commit() объявляет тип данных, то есть формально описывает коммуникационный буфер, но не содержимое этого буфера. Поэтому после того, как тип данных объявлен, он может быть многократно использован, чтобы передавать изменяемое содержимое буфера или различных буферов с различными стартовыми адресами. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 15 Удаление объектов типа данных int MPI_Type_free(MPI_Datatype datatype) Вход datatype: Тип данных, который освобождается Функция MPI_Type_free() маркирует объекты типа данных, связанные с datatype для удаления и установки типа данных в MPI_DATATYPE_NULL. Удаление типа данных не воздействует на другой тип, который был построен от удаленного типа. Система ведет себя как если бы аргументы входного типа данных были переданы конструктору производного типа данных по значению. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 16 Пример #include “mpi.h” #include <stdio.h> #include <stdlib.h> typedef struct { double real, img; } complex; typedef void complexproduct(complex *a, complex *b, int *len, MPI_Datatype *dptr) { int i; complex c; for (i=0; i<*len; i++) { c.real = b->real*a->real - b->img*a->img; c.img = b->real*a->img + b->img*a->real; *b = c; a++; b++ } } Параллельное программирование с использованием технологии MPI Аксёнов С.В. 17 Пример int main (int argc, char *argv[]) { complex sendbuf[5]; complex recvbuf[5], int i, n, rank; MPI_Datatype ctype; MPI_Op complexop; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); srand(rank); MPI_Type_contiguous(2,MPI_DOUBLE, &ctype); MPI_Type_commit(&ctype); MPI_Op_create(complexproduct, 1, &complexop) for (i=0; i<5; i++) { sendbuf[i].real = rand()%10; sendbuf[i].img = rand()%10; } Параллельное программирование с использованием технологии MPI Аксёнов С.В. 18 Пример for (i=0; i<n; i++) printf("Процесс № %d Значение элемента sendbuf на позиции %d равно %d + %d *i",rank,i,sendbuf[i].real, sendbuf[i].img); MPI_Reduce(sendbuf,recvbuf,n,ctype,complexop,0,MPI_COMM_WORLD); if (rank==0) for (i=0; i<n; i++) printf("Процесс № %d Значение элемента sendbuf на позиции %d равно %d + %d *i",rank,i,sendbuf[i].real, sendbuf[i].img); MPI_Op_free(&complexop); MPI_Type_free(&ctype); MPI_Finalize(); return 0: } Параллельное программирование с использованием технологии MPI Аксёнов С.В. 19