Функции коллективного обмена данными
Основные особенности и отличия от коммуникаций типа "точка-точка":
ü на прием и/или передачу работают одновременно ВСЕ задачи-абоненты указываемого коммуникатора;
ü коллективная функция выполняет одновременно и прием, и передачу; она имеет большое количество параметров, часть которых нужна для приема, а часть для передачи; в разных задачах та или иная часть игнорируется;
ü как правило, значения ВСЕХ параметров (за исключением адресов буферов) должны быть идентичными во всех задачах;
ü 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 ставится барьер.