Основные особенности и отличия от коммуникаций типа "точка-точка":
ü на прием и/или передачу работают одновременно ВСЕ задачи-абоненты указываемого коммуникатора;
ü коллективная функция выполняет одновременно и прием, и передачу; она имеет большое количество параметров, часть которых нужна для приема, а часть для передачи; в разных задачах та или иная часть игнорируется;
ü как правило, значения ВСЕХ параметров (за исключением адресов буферов) должны быть идентичными во всех задачах;
ü MPI назначает идентификатор для сообщений автоматически;
ü кроме того, сообщения передаются не по указываемому коммуникатору, а по временному коммуникатору-дупликату;
ü тем самым потоки данных коллективных функций надежно изолируются друг от друга и от потоков, созданных функциями "точка-точка".
MPI_Bcast рассылает содержимое буфера из задачи, имеющей в указанной области связи номер root, во все остальные:
MPI_Bcast(buf, count, dataType, rootRank, communicator);
Аргументы функции:
1.Адрес буфера, из которого в ветви №rootRank берутся, а в остальных задачах помещаются данные.
2.Размер буфера. Задается не в байтах, а в количестве ячеек. Указывает, сколько ячеек требуется передать и максимальную емкость приемного буфера.
3.Тип ячейки буфера. Для описания базовых типов Си в MPI определены константы MPI_INT, MPI_CHAR, MPI_DOUBLE и так далее, имеющие тип MPI_Datatype. Их названия образуются префиксом "MPI_" и именем соответствующего типа (int, char, double,...), записанным заглавными буквами.
4.Номер ветви, из которой происходит посылка данных. Все остальные ветви внутри коммуникатора communicator принимают данные.
5.Описатель области связи (коммуникатор).
Функция MPI_Bcast эквивалентна по результату (но не по внутреннему устройству) следующему фрагменту:
int commSize, myRank;
MPI_Comm_size(communicator, &commSize);
MPI_Comm_rank(communicator, &myRank);
if(myRank == rootRank)
for(i=0; i<commSize; i++)
MPI_Send(buf, count, dataType, i,
tempMsgTag, communicator);
MPI_Recv(buf, count, dataType, rootRank, tempMsgTag,
communicator, &status);
MPI_Gather (sendBuf, sendCount, sendType, recvBuf, recvCount, recvType, rootRank, communicator) ("совок") собирает в приемный буфер задачи root передающие буфера остальных задач. Ее аналог:
MPI_Send(sendBuf, sendCount, sendType, rootRank,...);
if(myRank == rootRank) {
MPI_Type_extent(recvType, &elemSize);
for(i=0; i<commSize; i++)
MPI_Recv(((char*))recvBuf) + (i * recvCount * elemSize),
recvCount, recvType, i,...);
}
Заметьте, что а) recvType и sendType могут быть разные и, таким образом, будут задавать разную интерпретацию данных на приемной и передающей стороне; б) задача-приемник также отправляет данные в свой приемный буфер.
Векторный вариант "совка" - MPI_Gatherv - позволяет задавать разное количество отправляемых данных в разных задачах-отправителях. Соответственно, на приемной стороне задается массив позиций в приемном буфере, по которым следует размещать поступающие данные, и максимальные длины порций данных от всех задач. Оба массива содержат позиции/длины не в байтах, а в количестве ячеек типа recvCount. Ее аналог:
MPI_Send(sendBuf, sendCount, sendType, rootRank,...);
if(myRank == rootRank) {
MPI_Type_extent(recvType, &elemSize);
for(i=0; i<commSize; i++)
MPI_Recv(((char*))recvBuf) + displs[i] * recvCounts[i]
* elemSize, recvCounts[i], recvType, i,...);
}
MPI_Scatter (sendBuf, sendCount, sendType, recvBuf, recvCount, recvType, rootRank, communicator) ("разбрызгиватель"): выполняет обратную "совку" операцию - части передающего буфера из задачи root распределяются по приемным буферам всех задач. Ее аналог:
if(myRank == rootRank) {
MPI_Type_extent(recvType, &elemSize);
for(i=0; i<commSize; i++)
MPI_Send(((char*)sendBuf) + i*sendCount*elemSize,
sendCount, sendType, i,...);
}
MPI_Recv(recvBuf, recvCount, recvType, rootRank,...);
И ее векторный вариант - MPI_Scatterv, рассылающая части неодинаковой длины в приемные буфера неодинаковой длины.
MPI_Allgather аналогична MPI_Gather, но прием осуществляется не в одной задаче, а во всех: каждая имеет специфическое содержимое в передающем буфере, и все получают одинаковое содержимое в буфере приемном. Как и в MPI_Gather, приемный буфер последовательно заполняется данными изо всех передающих. Вариант с неодинаковым количеством данных называется MPI_Allgatherv.
MPI_Alltoall: каждый процесс нарезает передающий буфер на куски и рассылает куски остальным процессам; каждый процесс получает куски от всех остальных и поочередно размещает их приемном буфере. Это "совок" и "разбрызгиватель" в одном флаконе. Векторный вариант называется MPI_Alltoallv.
Коллективные функции несовместимы с "точка-точка": недопустимым, например, является вызов в одной из принимающих широковещательное сообщение задач MPI_Recv вместо MPI_Bcast.
Точки синхронизации
Этим занимается всего одна функция:
int MPI_Barrier(MPI_Comm comm);
MPI_Barrier останавливает выполнение вызвавшей ее задачи до тех пор, пока не будет вызвана из всех остальных задач, подсоединенных к указываемому коммуникатору. Гарантирует, что к выполнению следующей за MPI_Barrier инструкции каждая задача приступит одновременно с остальными.
Это единственная в MPI функция, вызовами которой гарантированно синхронизируется во времени выполнение различных ветвей. Некоторые другие коллективные функции в зависимости от реализации могут обладать, а могут и не обладать свойством одновременно возвращать управление всем ветвям; но для них это свойство является побочным и необязательным - если Вам нужна синхронность, используйте только MPI_Barrier.
Синхронизация может потребоваться перед аварийным завершением: там ветвь 0 рапортует об ошибке, и чтобы ни одна из оставшихся ветвей вызовом MPI_Abort не завершила нулевую досрочно-принудительно, перед MPI_Abort ставится барьер.