
Лекции / Лекция 9
.odtТехнологии и методы программирования. Лекция 9.
Обмен сообщениями. Коммуникаторы.
Коммуникатор определяет группу процессов и их контекст выполнения. По умолчанию все созданные процессы принадлежат к MPI_COMM_WORLD, но можно создать и свой собственный коммуникатор. Следует помнить, что обмен сообщениями возможен только между процессами одного коммуникатора.
Для получения количества процессов у коммуникатора используется функция MPI_Comm_size(MPI_Comm comm, &size), для получения номера-ранга процесса - MPI_Comm_rank(MPI_Comm comm, &rank). Ранг крайне важен при управлении работой процессов.
Рассмотрим передачу сообщений между процессами. Для простой отправки сообщения используется функция MPI_Send(void* buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm), где buf - область памяти, где лежит сообщение, count — число элементов в сообщении, type — один из внутренних типов элементов MPI, dest — ранг процесса-получателя, tag — идентификатор сообщения, используемый для фильтрации сообщений получателем, comm — коммуникатор. После завершения передачи буфер можно использовать для передачи другого сообщения, так как пока сообщение не будет принято — функция не завершиться, и выполнение не будет продолжено.
Базовые типы данных:
Тип MPI |
Соответствующий тип С |
MPI_CHAR |
signed char |
MPI_SHORT |
signed short int |
MPI_INT |
signed int |
MPI_LONG |
signed long int |
MPI_UNSIGNED_CHAR |
unsigned char |
MPI_UNSIGNED_SHORT |
unsigned short int |
MPI_UNSIGNED |
unsigned int |
MPI_UNSIGNED_LONG |
unsigned long int |
MPI_FLOAT |
float |
MPI_DOUBLE |
double |
MPI_LONG_DOUBLE |
long double |
MPI_BYTE |
- |
MPI_PACKED |
- |
Приём сообщений осуществляется функцией MPI_Recv(void *buf, int count, MPI_Datatype type, int source, int tag, MPI_Comm comm, MPI_Status *status), где source — ранг процесса-отправителя, status — указатель на структуру данных, в которой содержится информация о результатах выполнения приёма сообщения. При приёме буфер должен быть достаточно велик для сообщения (может быть и больше), а тип элементов должен совпадать с типом, указанном при отправке. Если необходимо принять сообщение от любого процесса, указывается source=MPI_ANY_SOURCE, а для приёма сообщения с любым тэгом — tag=MPI_ANY_TAG. После приёма сообщения ранг отправителья находится в status.MPI_SOURCE, тэг — аналогично, status.MPI_TAG. Для определения количества посланных элементов используется функция MPI_Get_count(MPI_Status *status, MPI_DATA_TYPE type, int *count).
Функция MPI_Recv может вызываться до или после MPI_Send, но при этом работа процесса-получателя всегда приостанавливается до приёма сообщения.
При распределённых вычислениях, при рассылке сообщений иногда теряется детерминированность:
Пример:
int main(int argc, char* argv[]){
Описание Proc_Num, Proc_Rank, status.
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORD, *Proc_Num);
MPI_Comm_rank(MPI_COMM_WORLD, *Proc_Rank);
if (Proc_Rank==0){
printf(“Выполняется процесс ”, Proc_Rank);
for (int i=1; i<Proc_Num-1; i++){
MPI_Recv(&Proc_Rank, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
printf(“”, Prov_Rank);
}
}
MPI_Send(&Proc_Rank, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
MPI_Finalize();
return 0;
Какое сообщение будет выведено первым? Неизвестно, это зависит от того, какой из потоков первым выполнится, то есть присутствует состояние гонки между процессами. Чтобы приём шёл строго по порядку, в функции MPI_Recv вместо MPI_ANY_SOURCE необходимо указать I. Тогда порядок приёма будет зависеть от ранга, а не от фактического порядка поступления сообщений.