MPI - отправка сообщений процессам, которые запускаются из других функций - PullRequest
0 голосов
/ 04 мая 2020

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

...

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

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

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

Main выглядит следующим образом

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), но структура программы должна использоваться, как описано выше.

Когда программа запускается MASTER process инициирует игру и вызывает минимакс, чтобы получить оптимальный ход для максимизирующего игрока.

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

Ответы [ 2 ]

1 голос
/ 05 мая 2020

Для ясности, MPI - это структурированная коммуникационная библиотека, а не какое-то эзотерическое расширение c для языка параллельного программирования. Это просто облегчает структурированный обмен данными между сущностями, называемыми ranks . Обычно ранги - это процессы, выполняющиеся на одном и том же компьютере или на отдельных компьютерах, связанных с какой-либо сетью, но это могут быть и другие виды взаимодействующих объектов. Важно то, что каждый ранг сам по себе важен, когда речь заходит о выполнении кода, и ему все равно, где находятся другие ранги в программе. Более того, все равно, работают ли другие ряды в одной программе. Фактически, хотя для MPI характерно, чтобы все ранги выполняли один и тот же код, так называемый SPMD или S ingle P rogram M ulitple D ata, вы можете написать отдельную программу для группы рангов или даже для каждого ранга, которая называется MPMD или M ultiple P rograms M ultiple D ata. MPI даже облегчает классический режим клиент-сервер и позволяет соединять отдельные задания MPI. SPMD проще программировать, так как вам нужно написать только одну программу.

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

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

  • отправитель: ранг A готов отправить данные на ранг B и поэтому вызывает 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 и могут обмениваться сообщениями.

0 голосов
/ 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!

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

#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 возвращал информацию, необходимую главному процессу для широковещательной передачи, а затем широковещательную передачу вне условной ветви.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...