Как транслировать элементы только определенным рангам без использования подпрограмм MPI_Send и MPI_Recv

Просто общий вопрос:

Я хотел спросить, есть ли возможность передавать элементы только определенным рангам в MPI без использования подпрограмм MPI_Send и MPI_Recv.


person user47    schedule 19.01.2021    source источник
comment
Это не то, как MPI должен работать. При этом вы можете создать специальный коммуникатор только с этими рангами, а затем MPI_Bcast(). Или вы можете использовать MPI_Scatterv() (хотя другие элементы все равно должны вызывать MPI_Scatterv(..., recvcount=0, ...).   -  person Gilles Gouaillardet    schedule 20.01.2021


Ответы (1)


Я хотел спросить, есть ли возможность передавать элементы только определенным рангам в MPI без использования MPI_Send MPI_Recv.

Начнем с описания MPI_Bcast процедура.

Рассылает сообщение от процесса с корневым рангом всем остальным процессам коммуникатора

Подпрограмма широковещательной рассылки MPI_Bcast представляет собой коллективную коммуникацию. Следовательно:

Коллективная коммуникация — это метод коммуникации, предполагающий участие всех процессов в коммуникаторе.

Обратите внимание на текст, выделенный полужирным шрифтом, т. е. все процессы в коммуникаторе. Следовательно, один из подходов (для достижения желаемого) заключается в создании подмножества, состоящего из процессов, которые будут участвовать в подпрограмме широковещания. Это подмножество может быть реализовано путем создания нового коммуникатора< /а>. Для создания этого коммуникатора можно использовать функцию MPI MPI_Comm_split. Об этой процедуре из источника можно прочитать:

Как следует из названия, MPI_Comm_split создает новые коммуникаторы, "разделяя" коммуникатор на группу подчиненных коммуникаторов на основе входных значений цвета и ключа. Здесь важно отметить, что исходный коммуникатор не уходят, но для каждого процесса создается новый коммуникатор.
Первый аргумент, comm, это коммуникатор, который будет использоваться в качестве основы для новых коммуникаторов. Это может быть MPI_COMM_WORLD, но может быть и любой другой коммуникатор.
Второй аргумент, цвет, определяет, какому новому коммуникатору будет принадлежать каждый процесс. Все процессы, передающие одно и то же значение цвета, назначаются одному и тому же коммуникатору. Если цвет MPI_UNDEFINED, этот процесс не будет включен ни в один из новых коммуникаторов. Третий аргумент, ключ, определяет порядок (ранг) в каждом новом коммуникаторе. Процесс, который передает наименьшее значение для ключа, будет иметь ранг 0, следующий наименьший будет иметь ранг 1 и так далее. Если есть ничья, процесс, который имел более низкий ранг в исходном коммуникаторе, будет первым. Последний аргумент, newcomm, — это то, как MPI возвращает пользователю новый коммуникатор.

Допустим, мы хотели, чтобы в MPI_Bcast участвовали только процессы с четным рангом; Сначала мы создадим коммуникатор:

MPI_Comm new_comm;
int color = (world_rank % 2  == 0) ? 1 : MPI_UNDEFINED;
MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &new_comm);

и, в конце концов, вызовите MPI_Bcast для нового коммуникатора:

    if(world_rank % 2  == 0){
        ....
        MPI_Bcast(&bcast_value, 1, MPI_INT, 0, new_comm);
        ...
   }

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

    MPI_Comm_free(&new_comm);

Пример работающего кода:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

int main(int argc,char *argv[]){
    MPI_Init(NULL,NULL); // Initialize the MPI environment
    int world_rank; 
    int world_size;
    MPI_Comm_rank(MPI_COMM_WORLD,&world_rank);
    MPI_Comm_size(MPI_COMM_WORLD,&world_size);
    int bcast_value = world_rank;  
    MPI_Bcast(&bcast_value, 1, MPI_INT, 0, MPI_COMM_WORLD);
    printf("MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = %d, bcast_value = %d \n", world_rank, bcast_value);

    MPI_Comm new_comm;
    int color = (world_rank % 2  == 0) ? 1 : MPI_UNDEFINED;
    MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &new_comm); 
   
    if(world_rank % 2  == 0){
    int new_comm_rank, new_comm_size;
    MPI_Comm_rank(new_comm, &new_comm_rank);
        MPI_Comm_size(new_comm, &new_comm_size);
        bcast_value = 1000;
        MPI_Bcast(&bcast_value, 1, MPI_INT, 0, new_comm);
    
    printf("MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = %d, new_comm = %d, bcast_value = %d \n", world_rank, new_comm_rank,  bcast_value);
        MPI_Comm_free(&new_comm);
   }
   MPI_Finalize(); 
   return 0;
 }

В этом примере кода показаны два вызова MPI_Bcast, один со всеми процессами MPI_COMM_WORLD (т. е. MPI_Bcast 1), а другой — только с подмножеством этих процессов (т. е. MPI_Bcast 2). .

Вывод (для 8 процессов):

MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 0, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 4, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 5, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 6, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 7, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 1, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 2, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 3, bcast_value = 0 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 0, new_comm = 0, bcast_value = 1000 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 4, new_comm = 2, bcast_value = 1000 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 2, new_comm = 1, bcast_value = 1000 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 6, new_comm = 3, bcast_value = 1000
person dreamcrash    schedule 28.01.2021