Параллельное программирование с использованием технологии MPI Лекция 5 Томский политехнический университет Аксёнов Сергей Владимирович к.т.н., доцент каф.ОСУ ТПУ Параллельное программирование с использованием технологии MPI Аксёнов С.В. 1 Атрибуты сообщения Селекция сообщения Для получения сообщения процессу необходимо знать: - идентификатор процесса отправителя - тег сообщения - коммуникатор процесса отправителя Процесс-получатель может задавать значение MPI_ANY_SOURCE для отправителя и/или значение MPI_ANY_TAG для тэга, определяя, что любой отправитель и/или тэг разрешен. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 2 Статусная информация Статусная информация – структура, содержащая три поля MPI_SOURCE MPI_TAG MPI_ERROR. Возвращение числа полученных элементов int MPI_Get_count (MPI_Status *status, MPI_Datatype datatype, int *count) Вход status: Вход datatype: Выход count: Статусная информация Тип элементов в приёмном буфере Количество полученных единиц Параллельное программирование с использованием технологии MPI Аксёнов С.В. 3 Стандартный блокирующий режим Посылка может стартовать вне зависимости от того, выполнен ли соответствующий прием. Она может быть завершена до окончания приема. В этом режиме решение о том, будет ли исходящее сообщение буферизовано или нет, принимает MРI. MРI может буферизовать исходящее сообщение. В таком случае операция посылки может завершиться до того, как будет вызван соответствующий прием. С другой стороны, буферное пространство может отсутствовать или MРI может отказаться от буферизации исходящего сообщения из-за ухудшения характеристик обмена. В этом случае вызов send не будет завершен, пока данные не будут перемещены в процесс-получатель. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 4 Стандартный блокирующий режим int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) Параллельное программирование с использованием технологии MPI Аксёнов С.В. 5 Буферизированный режим посылки Передача сообщения в буферизованном режиме может быть начата независимо от того, зарегистрирован ли соответствующий прием. Источник копирует сообщение в буфер, а затем передает его в неблокирующем режиме, так же как в стандартном режиме. Эта операция локальна, поскольку ее выполнение не зависит от наличия соответствующего приема. Если объем буфера недостаточен, возникает ошибка. Выделение буфера и его размер контролируются программистом. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 6 Буферизированный режим посылки int MPI_Bsend(void *buf, int count, MPI_Datatype datatype,int dest, int tag, MPI_Comm comm) Параллельное программирование с использованием технологии MPI Аксёнов С.В. 7 Пример: необгоняемые сообщения MPI_Comm_rank( MPI_COMM_WORLD, &rank ); if (rank == 0) { MPI_Bsend(&buf1,10,MPI_INT,1,25,MPI_COMM_WORLD); MPI_Bsend(&buf2,20,MPI_DOUBLE,1,26,MPI_COMM_WORLD); } if (rank==1) { MPI_Recv(&source1, 10, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status); MPI_Recv(&source2, 20, MPI_DOUBLE, 0, 26, MPI_COMM_WORLD, &status); } Процесс 1. Отправка сообщения buf1. Буферизирование buf1 Процесс 2. Получение данных из буфера buf1→source1 Процесс 1. Отправка сообщения buf2. Буферизирование buf2 Процесс 2. Получение данных из буфера buf2 →source2 Параллельное программирование с использованием технологии MPI Аксёнов С.В. 8 Распределение и использование буферов Выделение буфера и его размер контролируются программистом. Размер буфера должен превосходить размер сообщения на величину MPI_BSEND_OVERHEAD. Установка буфера int MPI_Buff_attach(void *buf, int size) Вход buf: Начальный адрес буфера Вход size: Размер буфера в байтах Назначение: Создание буфера buf размером size байтов Отключение буфера int MPI_Buff_detach(void *buf, int *size) Выход buf: Начальный адрес буфера Выход size: Размер буфера в байтах Назначение: Отключение буфера. Получение адреса buf и размера отключаемого буфера buf. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 9 Пример int *buffer; int rank; MPI_Status status; int buffsize = 40; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) { buffer = (int *) malloc(buffsize*sizeof(int) + MPI_BSEND_OVERHEAD); MPI_Buffer_attach(buffer, buffsize); //Заполнение buffer MPI_Bsend(buffer, buffsize, MPI_INT, 1, 25, MPI_COMM_WORLD); MPI_Buffer_detach(&buffer, &buffsize); } else if (rank==1) MPI_Recv(buffer, buffsize, MPI_INT, 0, 25, MPI_COMM_WORLD, &status); MPI_Finalize(); Параллельное программирование с использованием технологии MPI Аксёнов С.В. 10 Синхронный режим Посылка может стартовать вне зависимости от того, был ли начат соответствующий прием. Посылка будет завершена успешно, только если соответствующая операция приема стартовала. Завершение синхронной передачи не только указывает что буфер отправителя может быть повторно использован, но также и отмечает, что получатель начал выполнение приема. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 11 Синхронный режим int MPI_Ssend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) Параллельное программирование с использованием технологии MPI Аксёнов С.В. 12 Пример: пересекающиеся пары MPI_Comm_rank( MPI_COMM_WORLD, &rank ); if (rank == 0) { MPI_Bsend(&buf1,20,MPI_INT,1,25,MPI_COMM_WORLD); MPI_Ssend(&buf2,20,MPI_INT,1,26,MPI_COMM_WORLD); } else if (rank==1) { MPI_Recv(&source1, 20, MPI_INT, 0, 26, MPI_COMM_WORLD, &status); MPI_Recv(&source2, 20, MPI_INT, 0, 25, MPI_COMM_WORLD, &status); } Процесс 1. Отправка сообщения buf1. Буферизирование buf1 Процесс 1. Отправка сообщения buf2. Процесс 2. Прием сообщения buf2→source1 Процесс 2. Получение данных из буфера buf1 →source2 Параллельное программирование с использованием технологии MPI Аксёнов С.В. 13 Обмен по готовности Посылка может быть запущена только тогда, когда прием уже инициирован. В противном случае операция является ошибочной, и результат будет неопределенным. Завершение операции посылки не зависит от состояния приема и указывает, что буфер посылки может быть повторно использован. Операция посылки, которая использует режим готовности, имеет ту же семантику, как и стандартная или синхронная передача. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 14 Обмен по готовности int MPI_Rsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) Параллельное программирование с использованием технологии MPI Аксёнов С.В. 15 Неблокирующая передача-приём Неблокирующий вызов инициирует операцию посылки, но не завершает ее. Вызов начала посылки будет возвращать управление перед тем, как сообщение будет послано из буфера отправителя. Отдельный вызов для завершения посылки необходим, чтобы завершить обмен, то есть убедиться, что данные уже извлечены из буфера отправителя. Неблокирующий вызов инициирует операцию приема, но не завершает ее. Вызов будет закончен до записи сообщения в приемный буфер. Необходим отдельный вызов завершения приема, чтобы завершить операцию приема и проверить, что данные получены в приемный буфер. Посылка данных в память получателя может выполняться параллельно с вычислениями, производимыми после того, как прием был инициирован, и до его завершения. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 16 Фнкции инициации обмена Передача int MPI_Isend (void* buf, int count, MPI_Datatype datatype, int dest, int tag,MPI_Comm comm, MPI_Request *request) int MPI_Ibsend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) int MPI_Issend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) int MPI_Irsend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) Приём int MPI_Irecv (void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request) Выход request: Запрос обмена Остальные аргументы аналогичны аргументам функций блокирующего приёма/ блокирующей передачи Параллельное программирование с использованием технологии MPI Аксёнов С.В. 17 Завершение обмена Завершение операции посылки указывает, что отправитель теперь может изменять содержимое ячеек буфера посылки (операция посылки сама не меняет содержание буфера). Операция завершения не извещает, что сообщение было получено, но дает сведения, что оно было буферизовано коммуникационной подсистемой. Завершение операции приема указывает, что приемный буфер содержит принятое сообщение, что процессполучатель теперь может обращаться к нему. Параллельное программирование с использованием технологии MPI Аксёнов С.В. 18 Функции завершения обмена Подпрограмма MPI_Wait блокирует работу процесса до завершения приема или передачи сообщения. int MPI_Wait (MPI_Request *request, MPI_Status *status) Вход/Выход request : Запрос обмена Выход status: Статус Подпрограмма MPI_Test выполняет неблокирующую проверку завершения приема или передачи сообщения. int MPI_Test (MPI_Request *request, int *flag, MPI_Status *status) Вход/Выход request: Запрос обмена Выход flag: 1 – если операция завершена Выход status: Статус Параллельное программирование с использованием технологии MPI Аксёнов С.В. 19 Пример MPI_Request request; MPI_Status status; MPI_Comm_rank( MPI_COMM_WORLD, &rank ); if (rank == 0) { MPI_Isend(&buf1,20,MPI_INT,1,25,MPI_COMM_WORLD, &request); MPI_Wait(&request, &status) } if (rank==1) { MPI_Irecv(&source2, 20, MPI_INT, 0, 25, MPI_COMM_WORLD, &request); MPI_Wait(&request, &status); } Параллельное программирование с использованием технологии MPI Аксёнов С.В. 20 Задача 1 MPI_Comm_rank( MPI_COMM_WORLD, &rank ); if (rank == 0) { MPI_Send(&buf1,20,MPI_INT,1,25,MPI_COMM_WORLD); MPI_Recv(&source1, 20, MPI_INT, 1, 25, MPI_COMM_WORLD, &status); } if (rank==1) { MPI_Recv(&source2, 20, MPI_INT, 0, 25, MPI_COMM_WORLD, &status); MPI_Send(&buf2,20,MPI_INT, 0,25,MPI_COMM_WORLD); } Параллельное программирование с использованием технологии MPI Аксёнов С.В. 21 Задача 2 MPI_Comm_rank( MPI_COMM_WORLD, &rank ); if (rank == 0) { MPI_Recv(&source1, 20, MPI_INT, 1, 25, MPI_COMM_WORLD, &status); MPI_Send(&buf1,20,MPI_INT,1,25,MPI_COMM_WORLD); } if (rank==1) { MPI_Recv(&source2, 20, MPI_INT, 0, 25, MPI_COMM_WORLD, &status); MPI_Send(&buf2,20,MPI_INT, 0,25,MPI_COMM_WORLD); } Параллельное программирование с использованием технологии MPI Аксёнов С.В. 22 Задача 3 MPI_Comm_rank( MPI_COMM_WORLD, &rank ); if (rank == 0) { MPI_Send(&buf1,20,MPI_INT,1,25,MPI_COMM_WORLD); MPI_Recv(&source1, 20, MPI_INT, 1, 25, MPI_COMM_WORLD, &status); } if (rank==1) { MPI_Send(&buf2,20,MPI_INT, 0,25,MPI_COMM_WORLD); MPI_Recv(&source2, 20, MPI_INT, 0, 25, MPI_COMM_WORLD, &status); } Параллельное программирование с использованием технологии MPI Аксёнов С.В. 23