Коллективные функции для приема/передачи сообщений между процессами


 

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

 

Функция синхронизации процессов MPI_Barrier блокирует работу вызвавшего ее процесса до тех пор, пока все другие процессы группы также не вызовут эту функцию. Завершение работы этой функции возможно только всеми процессами одновременно (все процессы "преодолевают барьер" одновременно):

 

int MPI_Barrier(MPI_Comm comm);

 

где:

comm – идентификатор группы.

 

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

 

Функция рассылки данных каким либо одним процессом всем процессам группы:

 

int MPI_Bcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm);

 

где:

OUT buf – адрес начала буфера приема/посылки сообщения;

count – число передаваемых элементов в сообщении;

datatype – тип передаваемых элементов;

root – номер рассылающего процесса;

comm – идентификатор группы.

 

Графическая интерпретация операций при вызове функции MPI_Bcast представлена на Рис.1.

 

 

Рис.1. Графическая интерпретация операций при вызове функции MPI_Bcast.

 

Функция сборки блоков данных, посылаемых всеми процессами группы, в массив одного процесса этой же группы:

 

int MPI_Gather(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);

 

где:

sendbuf – адрес начала размещения посылаемых данных;

sendcount – число посылаемых элементов;

sendtype – тип посылаемых элементов;

OUT recvbuf – адрес начала буфера приема (используется только на процессе-получателе с номером root);

recvcount – число элементов, получаемых от каждого процесса (используется только на процессе-получателе с номером root);

recvtype – тип получаемых элементов;

root – номер процесса-получателя;

comm – идентификатор группы.

 

Функция MPI_Gather производит сборку блоков данных, посылаемых всеми процессами группы, включая процесс с номером root, в один массив процесса с номером root. Значения параметров sendcount, sendtype и root должны быть одинаковыми у всех процессов. Параметр recvbuf имеет значение только на собирающем процессе и на остальных процессах игнорируется. Тип посылаемых элементов sendtype должен совпадать с типом recvtype получаемых элементов, а число sendcount должно равняться числу recvcount. То есть, значение recvcount на процессе root – это число собираемых от каждого процесса элементов, а не общее количество собранных элементов.

 

Длина блоков данных для отправки предполагается одинаковой. Объединение происходит в порядке увеличения номеров процессов-отправителей. То есть данные, посланные процессом с номером i из своего буфера sendbuf, помещаются в i-ю порцию буфера recvbuf на процессе с номером root. Длина массива, в который собираются данные, должна быть достаточной для их размещения. Графическая интерпретация операций при вызове функции MPI_Gather представлена на Рис.2.

 

 

Рис.2. Графическая интерпретация операций при вызове функции MPI_Gather.

 

Пример программы с использованием функции MPI_Gather:

 

MPI_Comm comm;

int array[100], gsize;

int root, *rbuf;

//...

MPI_Comm_size(comm, &gsize);

rbuf = (int *) malloc(gsize * 100 * sizeof(int));

root = 0;

MPI_Gather(array, 100, MPI_INT, rbuf, 100, MPI_INT, root, comm);

 

Функция MPI_Allgather выполняется так же, как MPI_Gather, но получателями являются все процессы группы (поэтому в списке параметров отсутствует параметр root – номер процесса получателя). Данные, посланные процессом с номером i из своего буфера sendbuf, помещаются в i-ю порцию буфера recvbuf каждого процесса. После завершения операции содержимое буферов приема recvbuf у всех процессов одинаково.

 

int MPI_Allgather(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm);

 

где:

sendbuf – адрес начала буфера посылки;

sendcount – число посылаемых элементов;

sendtype – тип посылаемых элементов;

OUT recvbuf – адрес начала буфера приема;

recvcount – число элементов, получаемых от каждого процесса;

recvtype – тип получаемых элементов;

comm – идентификатор группы.

 

Функция MPI_Gatherv позволяет собирать блоки с разным числом элементов от каждого процесса, так как количество элементов, принимаемых от каждого процесса, задается индивидуально с помощью массива recvcounts. Эта функция обеспечивает также большую гибкость при размещении данных в процессе-получателе, благодаря введению в качестве параметра массива смещений displs.

 

int MPI_Gatherv(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* rbuf, int *recvcounts, int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm);

 

где:

sendbuf – адрес начала буфера передачи;

sendcount – число посылаемых элементов;

sendtype – тип посылаемых элементов;

OUT rbuf – адрес начала буфера приема;

recvcounts – целочисленный массив (размер равен числу процессов в группе), i-й элемент которого определяет число элементов, которое должно быть получено от процесса с номером i;

displs – целочисленный массив (размер равен числу процессов в группе), i-ое значение определяет смещение i-го блока данных относительно начала rbuf;

recvtype – тип получаемых элементов;

root – номер процесса-получателя;

comm – идентификатор группы.

 

Примечание. Смещение displs задается не в байтах, а по количеству элементов типа recvtype.

 

Сообщения помещаются в буфер приема процесса root в соответствии с номерами посылающих процессов, а именно, данные, посланные процессом с номером i, размещаются в адресном пространстве процесса root, начиная с адреса (rbuf +displs[i]). Графическая интерпретация операций при вызове функции MPI_Gatherv представлена на Рис.3.

 

 

Рис.3. Графическая интерпретация операций при вызове функции MPI_Gatherv.

 

 

Функция MPI_Allgatherv является аналогом функции MPI_Gatherv, но сборка выполняется всеми процессами группы (поэтому в списке параметров отсутствует параметр root – номер процесса получателя).

 

int MPI_Allgatherv(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* rbuf, int *recvcounts, int *displs, MPI_Datatype recvtype, MPI_Comm comm);

 

где:

sendbuf – адрес начала буфера передачи;

sendcount – число посылаемых элементов;

sendtype – тип посылаемых элементов;

OUT rbuf – адрес начала буфера приема;

recvcounts – целочисленный массив (размер равен числу процессов в группе), содержащий число элементов, которое должно быть получено от каждого процесса;

displs – целочисленный массив (размер равен числу процессов в группе), i-ое значение определяет смещение относительно начала rbuf i-го блока данных;

recvtype – тип получаемых элементов;

comm – идентификатор группы.

 

Примечание. Смещение displs задается не в байтах, а по количеству элементов типа recvtype.

 

Семейство функций распределения блоков данных по всем процессам группы состоит из двух функций: MPI_Scatter и MPI_Scaterv.

 

 

Функция MPI_Scatter разбивает сообщение из буфера посылки процесса root на равные части размером sendcount и посылает i-ю часть в буфер приема процесса с номером i (в том числе и самому себе). Процесс с номером root использует оба буфера (посылки и приема), поэтому на этом процессе все параметры являются существенными. Остальные процессы группы с идентификатором comm являются только получателями, поэтому для них параметры, специфицирующие буфер посылки, не существенны.

 

int MPI_Scatter(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);

 

где:

sendbuf – адрес начала размещения блоков распределяемых данных (используется только на процессе-отправителе с номером root);

sendcount – число элементов, посылаемых каждому процессу (используется только на процессе-отправителе с номером root);

sendtype – тип посылаемых элементов;

OUT recvbuf – адрес начала буфера приема;

recvcount – число получаемых элементов;

recvtype – тип получаемых элементов;

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

comm – идентификатор группы.

 

Тип посылаемых элементов sendtype должен совпадать с типом recvtype получаемых элементов, а число посылаемых элементов sendcount должно равняться числу принимаемых recvcount.

 

Примечание. Следует обратить внимание, что значение sendcount на процессе с номером root – это число посылаемых каждому процессу элементов, а не общее их количество.

 

Функция MPI_Scatter является обратной по отношению к MPI_Gather.

Графическая интерпретация операций при вызове функции MPI_Scatter представлена на Рис.4.

 

 

Рис.4. Графическая интерпретация операций при вызове функции MPI_Scatter.

 

 

Пример использования функции MPI_Scatter:

 

MPI_Comm comm;

int rbuf[100], gsize;

int root, *array;

//...

MPI_Comm_size(comm, &gsize);

array = (int *) malloc(gsize * 100 * sizeof(int));

root = 0;

//...

MPI_Scatter(array, 100, MPI_INT, rbuf, 100, MPI_INT, root, comm);

 

 

Функция MPI_Scaterv является векторным вариантом функции MPI_Scatter, позволяющим посылать каждому процессу различное количество элементов. Начало расположения элементов блока, посылаемого процессу с номером i, задается в массиве смещений displs, а число посылаемых элементов в массиве sendcounts. Эта функция является обратной по отношению к функции MPI_Gatherv.

 

int MPI_Scatterv(void* sendbuf, int *sendcounts, int *displs, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);

 

где:

sendbuf – адрес начала буфера посылки (используется только на процессе-отправителе с номером root);

sendcounts – целочисленный массив (размер равен числу процессов в группе), содержащий число элементов, посылаемых каждому процессу (используется только на процессе-отправителе с номером root);

displs – целочисленный массив (размер равен числу процессов в группе), i-ое значение определяет смещение относительно начала sendbuf для данных, посылаемых процессу с номером i (используется только на процессе-отправителе с номером root);

sendtype – тип посылаемых элементов;

OUT recvbuf – адрес начала буфера приема;

recvcount – число получаемых элементов;

recvtype – тип получаемых элементов;

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

comm – идентификатор группы.

 

Примечание. Смещение displs задается не в байтах, а по количеству элементов типа sendtype.

 

Графическая интерпретация операций при вызове функции MPI_Scatterv представлена на Рис.5.

 

 

Рис.5. Графическая интерпретация операций при вызове функции MPI_Scatterv.