3.4 Передачи данных между двумя процессами

Начнем рассмотрение средства взаимодействия в MPI с так называемых точечных обменов. Это название происходит от английского “point-to-point communications”, которое означает, что у взаимодействия есть две точки: процесс-отправитель и процесс-получатель сообщения. Точечные, так же как и обмены других типов, всегда производятся в пределах одного коммуникатора, который указывается в качестве параметра в вызовах функций обмена. Ранги процессов, принимающих участие в обменах, вычисляются по отношению к указанному коммуникатору. Ниже приведен пример программы, описывающий поведение двух взаимодействующих процессов, один из которых посылает данные, а другой их принимает и выводит на печать.

#include <stdio.h> 
#include "mpi.h" 
int main (int argc, char* argv[]){ 
    int rank; 
    MPI_Status st; 
    char buf[64]; 
    MPI_Init(&argc, &argv); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
    if (rank == 0) { 
        sprintf(buf, Hello from process 0); 
        MPI_Send(buf, 64, MPI_CHAR, 1, 0, MPI_COMM_WORLD); 
        } else { 
            MPI_Recv(buf, 64, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &st); 
            printf(Process %d received %s \n, rank, buf); 
        }; 
        MPI_Finalize(); 
        return 0; 
}

Выполнение данного примера приводит к следующему результату:

Process 1 received Hello from process 0

Рассмотрим подробнее текст примера. Вначале (строки 8, 9) с помощью функций, рассмотренных ранее, производится инициализация библиотеки и в переменной rank запоминается ранг процесса. Далее следует оператор if, первая ветвь (строки 11, 12) которого выполняется процессом с рангом 0, а вторая – всеми остальными процессами.

В результате действий, произведенных первым процессом в буфер buf помещается текст приветствия (строка 11), который затем (строка 12) пересылается процессу с рангом 1. Процесс 1 получает (строка 14) приветственное сообщение, сформированное и отправленное процессом 0, распечатывает его (строка 15).

Для передачи данных в примере, приведенном выше, применяются функции точечных обменов. Отправка сообщения осуществляется функцией:

int MPI_Send(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm);

В нашем примере пересылается весь массив buf. Так как массив состоит из 64 элементов, то в качестве параметров count и type указываются 64 и тип MPI_CHAR соответственно. Параметр dest определяет ранг процесса, которому предназначено сообщение. Параметр tag задает так называемый тэг сообщения, который представляет собой целое число, передаваемое вместе с сообщением и проверяемое при его приеме. В примере ранг процесса-получателя задается равным 1, а тэг 0. Последним параметром функции MPI_Send является коммуникатор, в пределах которого передается сообщение.

Для приема сообщения служит функция:

int MPI_Recv(void *buf, int count, MPI_Datatype type, int source, int tag, MPI_Comm comm, MPI_Status *status);

Первый параметр buf задает начало буфера для сохранения данных, пришедших сообщений. Второй параметр count задает максимальное количество элементов данных, которое предполагается принять. Третий параметр задает тип элементов данных. В качестве четвертого параметра source передается ранг процесса, от которого ожидается сообщение. Сообщения, пришедшие от процесса с другим рангом, не принимаются. Следующий параметр задает тэг ожидаемого сообщения. Так же как и в случае с рангом, сообщения, имеющие тэг, отличный от указанного, не принимаются. Предпоследний параметр comm – это коммуникатор, в пределах которого выполняется пересылка сообщения. Последний параметр status, который является указателем на структуру типа MPI_Status, которая заполняется после завершения операции приема и содержит информацию о том, каким образом завершилась операция.

В коммуникационных операциях типа точка-точка всегда участвуют не более двух процессов: передающий и принимающий. В MPI имеется множество функций, реализующих такой тип обменов. Многообразие объясняется возможностью организации таких обменов множеством способов. Описанные выше функции реализуют стандартный режим с блокировкой.

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

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

Как для блокирующих, так и неблокирующих операций MPI поддерживает четыре режима выполнения. Эти режимы касаются только функций передачи данных, поэтому для блокирующих и неблокирующих операций имеется по четыре функции посылки сообщения. В таблице 3.2 перечислены имена базовых коммуникационных функций типа “точка-точка”, имеющихся в библиотеке MPI.





Режимы выполненияС блокировкойБез блокировки



Стандартная посылка MPI_Send MPI_Isend



Синхронная посылка MPI_Ssend MPI_Issend



Буферизованная посылка MPI_Bsend MPI_Ibsend



Согласованная посылка MPI_Rsend MPI_Irsend



Прием информации MPI_Recv MPI_Irecv




Таблица 3.2: Функции типа “точка-точка”

Из таблицы хорошо виден принцип формирования имен функций. К именам базовых функций Send/Recv добавляются различные префиксы.

Префикс S (synchronous) – означает синхронный режим передачи данных. Операция передачи данных заканчивается только тогда, когда заканчивается прием данных. Функция нелокальная.

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

Префикс R (ready) – согласованный или подготовленный режим передачи данных. Операция передачи данных начинается только тогда, когда принимающий процессор выставил признак готовности приема данных, инициировав операцию приема. Функция нелокальная.

Префикс I (immediate) – относится к неблокирующим операциям.