MPI — отправка сообщений процессам, которые запускаются из других функций

У меня есть процесс с рангом 0 (MASTER), который выполняется в функции (FUNCA), которая выполняет:

...

get_moves_list(node,&moves_list,&moves_len,maximizing);
//for each rank in SLAVES
//MPI_Send a move to a SLAVE

Я хочу, чтобы подчиненные процессы получали сообщения от ГЛАВНОГО, но подчиненные процессы выполняются из/внутри другой функции (FUNCB)

void run_slave(rank) {
    int move;
    //MPI_Recv a move from MASTER
    //Do some stuff with that move
    //Then return to caller 
}

Главное выглядит так

int main(int argc,char **argv)
{
    int rank,size;
    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    MPI_Comm_size(MPI_COMM_WORLD,&size);

    if (rank == MASTER) {
        ...

        //CALL FUNCA

        ...
    } else {
        run_slave(rank);
        MPI_Finalize();
    }
}

Возможно ли что-то подобное с MPI, отправкой/получением сообщений процессам, работающим в разных функциях?

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

При запуске программы ГЛАВНЫЙ процесс инициирует игру и вызывает минимакс, чтобы получить оптимальный ход для максимизирующего игрока.

У меня работает последовательная версия minimax, и в настоящее время я пытаюсь распараллелить ее с помощью MPI, но пока безуспешно.


person it2901    schedule 04.05.2020    source источник
comment
Да, это возможно. MPI не важно, откуда вызывается функция.   -  person Gilles Gouaillardet    schedule 04.05.2020
comment
обратите внимание, что мастер-ранг также должен вызывать MPI_Finalize()   -  person Gilles Gouaillardet    schedule 04.05.2020
comment
Итак, если я использую MPI_Send из FUNCA, мне все еще нужно вызывать функцию run_slave() непосредственно после нее или я буду отправлять ее через функции? @GillesGouaillardet   -  person it2901    schedule 04.05.2020
comment
Я не понимаю вашего последнего комментария. FUNCA вызывается только главным рангом, поэтому, если он что-то отправляет, подчиненные должны запустить некоторый код для его получения. run_slave() выглядит хорошо.   -  person Gilles Gouaillardet    schedule 04.05.2020


Ответы (2)


Чтобы было понятно, MPI — это структурированная коммуникационная библиотека, а не какое-то эзотерическое расширение языка параллельного программирования. Он просто упрощает структурированный обмен данными между сущностями, называемыми рангами. Как правило, ранги — это процессы, работающие на одном компьютере или на разных компьютерах, связанных какой-либо сетью, но это также могут быть и другие взаимодействующие сущности. Важно то, что каждый ранг сам по себе, когда дело доходит до выполнения кода, и ему все равно, где в программе находятся другие ранги. Более того, ему все равно, работают ли другие ранги по той же программе. На самом деле, хотя для MPI характерно, что все ранги запускают один и тот же код, так называемая SPMD или Sодна программаM ultiple Data, вы можете написать отдельную программу для группы рангов или даже для каждого ранга, что известно как MPMD или Mmultiple P< /strong>программирует многонесколько данных. MPI даже упрощает классический режим клиент-сервер и позволяет соединяться отдельным заданиям MPI. SPMD просто легче программировать, так как вам нужно написать только одну программу.

Думайте о MPI просто как о посреднике (промежуточном программном обеспечении) между вашим кодом и специфичными для системы API, который обеспечивает простое взаимодействие между процессами и абстрагируется от таких вещей, как определение фактических конечных точек других партнеров по общению (например, определение сетевых адресов и номеров портов). когда связь осуществляется через TCP/IP). Когда вы пишете браузер, который связывается по сети с веб-сервером, вам все равно, какой код выполняет сервер. И наоборот, серверу все равно, какой код выполняет ваш браузер. Пока оба используют один и тот же протокол, связь работает. То же самое относится и к MPI — пока два ранга используют одну и ту же библиотеку MPI, они могут взаимодействовать.

Для успешной связи в MPI необходимы только две вещи (при типичном обмене данными точка-точка):

  • отправитель: ранг А желает отправить данные рангу Б, поэтому он вызывает MPI_Send(..., B, tag, MPI_COMM_SOMETHING);
  • получатель: ранг B желает получать данные от ранга A, поэтому он вызывает MPI_Recv(..., A, tag, MPI_COMM_SOMETHING, ...);

Пока оба ранга указывают один и тот же тег и коммуникатор, а адреса в вызовах отправки и приема совпадают попарно (включая возможность получателя указывать подстановочные знаки источника и тега), обмен будет происходить независимо от того, где находятся фактические строки кода. расположена.

Ниже приведен совершенно правильный пример MPI:

rank_0.c

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

int main(void)
{
   MPI_Init(NULL, NULL);

   int rank;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);

   int a;
   MPI_Recv(&a, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
   printf("Rank %d got %d from rank 1\n", rank, a);

   MPI_Finalize();
   return 0;
}

rank_1.c

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

int main(int argc, char **argv)
{
   MPI_Init(&argc, &argv);

   int rank;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);

   int a = 42;
   MPI_Send(&a, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
   printf("Rank %d sent %d to rank 0\n", rank, a);

   MPI_Finalize();
   return 0;
}

Скомпилируйте и запустите:

$ mpicc -o rank_0 rank_0.c
$ mpicc -o rank_1 rank_1.c
$ mpiexec -n 1 ./rank_0 : -n 1 ./rank_1
Rank 0 got 42 from rank 1
Rank 1 sent 42 to rank 0

Как видите, это две совершенно разные программы, и они по-прежнему успешно работают вместе в одном задании MPI и могут обмениваться сообщениями.

person Hristo Iliev    schedule 05.05.2020

Да, ты можешь это сделать. Вот полноценная игрушечная программа, которая должна демонстрировать эту функциональность:

#include <iostream>
#include "mpi.h"
#include "unistd.h"
#define MASTER 0
int pid, pnum;

void func1(void)
{
    int bcastval = 1000;
    MPI_Bcast(&bcastval, 1, MPI_INT, 0, MPI_COMM_WORLD);
    std::cout << pid << " sent " << bcastval << std::endl;
}

void func2(void)
{
    int recv;
    MPI_Bcast(&recv, 1, MPI_INT, 0, MPI_COMM_WORLD);
    std::cout << pid << " received " << recv << std::endl;
}


int main(void)
{
    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD,&pid);
    MPI_Comm_size(MPI_COMM_WORLD,&pnum);
    if (pid == MASTER) func1();
    else func2();
    if (pid == MASTER) std::cout << "Done!" << std::endl;
    MPI_Finalize();
}

Обратите внимание, что запуск с mpirun -np 2 ./a.out дает

0 sent 1000
1 received 1000
Done!

Однако я настоятельно рекомендую избегать этого, если у вас есть сложная логика внутри двух функций, вызываемых мастер-процессом и другими. Это легко проиллюстрировать на этом примере:

#include <iostream>
#include "mpi.h"
#include "unistd.h"
#define MASTER 0
int pid, pnum;

void func1(void)
{
    int bcastval = 1000;
    MPI_Bcast(&bcastval, 1, MPI_INT, 0, MPI_COMM_WORLD);
    std::cout << pid << " sent " << bcastval << std::endl;
    MPI_Barrier(MPI_COMM_WORLD); // any blocking call
}

void func2(void)
{
    int recv;
    MPI_Bcast(&recv, 1, MPI_INT, 0, MPI_COMM_WORLD);
    std::cout << pid << " received " << recv << std::endl;
}    

int main(void)
{
    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD,&pid);
    MPI_Comm_size(MPI_COMM_WORLD,&pnum);
    if (pid == MASTER) func1();
    else func2();
    if (pid == MASTER) std::cout << "Done!" << std::endl;
    MPI_Finalize();
}

Каждый раз, когда в ветви выполняется блокирующий вызов MPI, и не все процессы следуют за этой ветвью, MPI навсегда останавливает программу, поскольку не все процессы могут «отметиться» для вызова. Кроме того, если в этих функциях много чего происходит, может быть очень сложно устранить неполадки, поскольку вывод отладки может вести себя неожиданно.

Анекдот по этому поводу: я работаю над большим кодом мультифизического моделирования, который имеет слой MPI. Недавно произошло что-то похожее на описанное выше, и весь код был остановлен для всех 17 разработчиков. Каждый разработчик обнаружил, что код останавливается в другом месте, иногда во внешних библиотеках, зависящих от MPI. Устранение неполадок заняло много времени.

Я бы порекомендовал, чтобы FUNCA возвращал информацию, которую мастер-процесс должен транслировать, а затем транслировал вне условной ветки.

person wvn    schedule 05.05.2020
comment
Я думаю, что под блокировкой вызова MPI вы подразумеваете блокировку коллективного вызова MPI. - person Hristo Iliev; 05.05.2020