Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Техехин - Системное программное обеспечение - взаимодействие процессов (1114927), страница 27
Текст из файла (страница 27)
Самыми простыми из этих функцийявляются MPI_Wait() и MPI_Test():#include <mpi.h>166int MPI_Wait(MPI_Request *request, MPI_Status*status);int MPI_Test(MPI_Request *request, int *flag,MPI_Status *status);Функция MPI_Wait() вернет управление лишь тогда, когдаоперация приема/передачи сообщения, соответствующая квитанции,переданной в параметре request, будет завершена (для операцииотсылки, инициированной функцией MPI_Ibsend() это означает,что данные были скопированы в специально отведенный буфер).При этом для операции приема сообщения в структуре, на которуюуказывает параметр status, возвращается информация ополученном сообщении, аналогично тому, как это делается вMPI_Recv().
После возврата из функции MPI_Wait() для операцииотсылки сообщения процесс может повторно использовать буфер,содержавший тело отосланного сообщения, а для операции приемасообщения гарантируется, что после возврата из MPI_Wait() вбуфере для приема находится тело принятого сообщения.Для того, чтобы узнать, завершена ли та или иная операцияприема/передачи сообщения, но избежать блокирования в томслучае, если операция еще не завершилась, служит функцияMPI_Test(). В параметре flag ей передается адрес целочисленнойпеременной, в которой будет возвращено ненулевое значение, еслиоперация завершилась, и нулевое в противном случае.
В случае, есликвитанция соответствовала операции приема сообщения, и даннаяоперация завершилась, MPI_Test() заполняет структуру, накоторую указывает параметр status, информацией о принятомсообщении.Для того, чтобы узнать статус сразу нескольких асинхронныхопераций приема/передачи сообщений, служит ряд функций работыс массивом квитанций:#include <mpi.h>int MPI_Waitany(int count, MPI_Request*array_of_requests, int *index, MPI_Status *status);int MPI_Testany(int count, MPI_Request*array_of_requests, int *index, int *flag,MPI_Status *status);int MPI_Waitsome(int count, MPI_Request*array_of_requests, int *outcount, int*array_of_indices, MPI_Status *array_of_statuses);167int MPI_Testsome(int count, MPI_Request*array_of_requests, int *outcount, int*array_of_indices, MPI_Status *array_of_statuses);int MPI_Waitall(int count, MPI_Request*array_of_requests, MPI_Status *array_of_statuses);int MPI_Testall(int count, MPI_Request*array_of_requests, int *flag, MPI_Status*array_of_statuses);Все эти функции получают массив квитанций в параметреarray_of_requests и его длину в параметре count.
ФункцияMPI_Waitany() блокируется до тех пор, пока не завершится хотя быодна из операций, описываемых переданными квитанциями. Онавозвращает в параметре index индекс квитанции для завершившейсяоперации в массиве array_of_requests и, в случае, еслизавершилась операция приема сообщения, заполняет структуру, накоторую указывает параметр status, информацией о полученномсообщении. MPI_Testany() не блокируется, а возвращает впеременной, на которую указывает параметр flag, ненулевоезначение, если одна из операций завершилась. В этом случае онавозвращает в параметрах index и status то же самое, что иMPI_Waitany().Отметим, что если несколько операций из интересующегомножества завершаются одновременно, то и MPI_Waitany(), иMPI_Testany() возвращают индекс и статус лишь одной из них,выбранной случайным образом.
Более эффективным вариантом втакомслучаеявляетсяиспользованиесоответственноMPI_Waitsome() и MPI_Testsome(): их поведение аналогичноMPI_Waitany() и MPI_Testany() за тем исключением, что в случае,если одновременно завершается несколько операций, онивозвращают статус для всех завершившихся операций. При этом впараметре outcount возвращается количество завершившихсяопераций, в параметре array_of_indices – индексы квитанцийзавершившихся операций в исходном массиве квитанций, а массивarray_of_statuses содержит структуры типа MPI_Status,описывающие принятые сообщения (значения в массивеarray_of_statuses имеют смысл только для операций приемасообщения).
Отметим, что у функции MPI_Testany() нет параметраflag – вместо этого, она возвращает 0 в параметре outcount, еслини одна из операций не завершилась.168Функция MPI_Waitall() блокируется до тех пор, пока незавершатся все операции, квитанции для которых были ей переданы,и заполняет информацию о принятых сообщениях для операцийприема сообщения в элементах массива array_of_statuses (приэтом i-й элемент массива array_of_statuses соответствуетоперации, квитанция для которой была передана в i-м элементемассива array_of_requests). Функция MPI_Testall() возвращаетненулевое значение в переменной, адрес которой указан в параметреflag, если завершились все операции квитанции для которых былией переданы, и нулевое значение в противном случае.
При этом,если все операции завершились, она заполняет элементы массиваarray_of_statusesаналогичнотому,какэтоделаетMPI_Waitall().Пример 32. MPI: коммуникации «точка-точка». «Пингпонг».В этом примере рассматривается работа в неблокирующемрежиме. Процессы посылают друг другу целое число, всякий разувеличивая его на 1. Когда число достигнет некоего максимума,переданного в качестве параметра командной строки, все ветвизавершаются.
Для того, чтобы указать всем последующим ветвям нато, что пора завершаться, процесс, обнаруживший достижениемаксимума, обнуляет число.#include <mpi.h>#include <stdio.h>int main(int argc, char **argv){int size, rank, max, i = 1, j, prev, next;MPI_Request recv_request, send_request;MPI_Status status;MPI_Init(&argc,библиотеку */&argv);/*ИнициализируемMPI_Comm_size(MPI_COMM_WORLD, &size);/*Узнаемколичествоприложении... */задачвзапущенномMPI_Comm_rank (MPI_COMM_WORLD, &rank);/* ...и свой собственный номер: от 0 до (size1) */169/* определяем максимум */if ((argc < 2) || ((max = atoi(argv[1])) <= 0)){printf("Bad arguments!\n");MPI_Finalize();return 1;}/* каждая ветвь определяет, кому посылать и откого принимать сообщения */next = (rank + 1) % size;prev = (rank == 0) ? (size - 1) : (rank - 1);if (rank == 0) {/* ветвь с номером 0 начинает «пинг-понг»*/MPI_Isend(&i,1,MPI_INT,1,MPI_COMM_WORLD, &send_request);printf("process %d sent valueprocess %d\n", rank, i, next);next,\"%d\"toMPI_Wait(&send_request, NULL);}while ((i > 0) && (i < max)) {MPI_Irecv(&i,MPI_ANY_TAG,&recv_request);1,MPI_INT,prev,MPI_COMM_WORLD,MPI_Wait(&recv_request, &status);if (i > 0) {printf("process%dreceivedvalue\"%d\" " + "from process %d\n", rank,i++, prev);if (i == max) {i = 0;}MPI_Isend(&i, 1, MPI_INT, next,MPI_COMM_WORLD, &send_request);1700,printf("process %d sent value \"%d\"" + " to process %d\n", rank, i,next);MPI_Wait(&send_request, NULL);} else if (i == 0) {MPI_Isend(&i, 1, MPI_INT, next,MPI_COMM_WORLD, &send_request);0,MPI_Wait(&send_request, NULL);break;}}MPI_Finalize();return 0;}7.2.7 Коллективные коммуникации.Как уже говорилось, помимо коммуникаций «точка-точка»,MPI предоставляет различные возможности для осуществленияколлективных операций, в которых участвуют все ветвиприложения, входящие в определенный коммуникационныйконтекст.
К таким операциям относятся рассылка данных отвыделенной ветви всем остальным, сбор данных от всех ветвей наодной выделенной ветви, рассылка данных от всех ветвей ко всем, атакже коллективная пересылка данных, совмещенная с иходновременной обработкой.Отметим, что хотя все те же самые действия можнозапрограммировать с использованием коммуникаций «точка-точка»,однако более целесообразным является применение коллективныхфункций, и тому есть несколько причин. Во-первых, во многихслучаях использование коллективной функции может бытьэффективнее, нежели моделирование ее поведения при помощифункций обмена «точка-точка», так как реализация коллективныхфункций может содержать некоторую оптимизацию (например, прирассылке всем ветвям одинаковых данных может использоваться непоследовательная посылка всем ветвям сообщений от однойвыделенной ветви, а распространение сообщения между ветвями попринципу двоичного дерева).
Кроме того, при использованииколлективных функций сводится к нулю риск программной ошибки,связанной с тем, что сообщение из коллективного потока данныхбудет перепутано с другим, индивидуальным сообщением171(например, получено вместо него при использовании константMPI_ANY_SOURCE, MPI_ANY_TAG в качестве параметров функцииприема сообщения). При передаче сообщений, формируемыхколлективными функциями, используется временный дубликатзаданного коммуникатора, который неявно создается библиотекойна время выполнения данной функции и уничтожаетсяавтоматически после ее завершения. Таким образом, сообщения,переданные коллективной функцией, могут быть получены в другихили в той же самой ветви только при помощи той же самойколлективной функции.Важной особенностью любой коллективной функции являетсято, что ее поведение будет корректным только в том случае, если онабыла вызвана во всех ветвях, входящих в заданныйкоммуникационный контекст.
В некоторых (или во всех) ветвях припомощи этой функции данные будут приниматься, в некоторых (иливо всех) ветвях – отсылаться. Иногда с помощью одного вызовапроисходит одновременно и отправка, и прием данных. Как правило,все ветви должны вызвать коллективную функцию с одними и темиже значениями фактических параметров (за исключениемпараметров, содержащих адреса буферов – эти значения, разумеется,в каждой ветви могут быть своими). Все коллективные функцииимеют параметр-коммуникатор, описывающий коммуникационныйконтекст, в котором происходит коллективный обмен.Семантика буферизации для коллективных функцийаналогична стандартному режиму буферизации для одиночныхкоммуникаций в блокирующем режиме: возврат управления изколлективной функции означает, что вызвавшая ветвь можетповторно использовать буфер, в котором содержались данные дляотправки (либо гарантирует, что в буфере для приема находятсяпринятые данные).
При этом, отнюдь не гарантируется, что востальных ветвях к этому моменту данная коллективная операциятакже завершилась (и даже не гарантируется, что она в них уженачалась). Таким образом, хотя в некоторых случаях возврат изколлективных функций действительно происходит во всех ветвяходновременно, нельзя полагаться на этот факт и использовать егодля целей синхронизации ветвей. Для явной синхронизации ветвейможет использоваться лишь одна специально предназначеннаяколлективная функция – это описанная выше функцияMPI_Barrier().172Коллективный обмен данными.Для отправки сообщений от одной выделенной ветви всемветвям, входящим в данный коммуникационный контекст, служатфункции MPI_Bcast() и MPI_Scatter().