У меня есть следующая программа, которая компилируется и запускается с openmpi и mpich-3.2.1, но при вызове MPI_Waitany
с mpich-3.3 появляется тупик.
Программа должна выполняться с четным числом рангов, половина из которых отправляет данные во вторую половину через интеркоммуникатор.
Это гораздо меньшая версия, чем большая пользовательская программа моделирования, я попытался сделать минимальный пример.
Одна странная вещь заключается в том, что увеличение переменной nwork
до 2 приводит к исчезновению тупика.
#include <cstdio>
#include <mpi.h>
#include <vector>
const int endMsg = -1;
const int endTag = 424242;
class Work
{
public:
Work(const MPI_Comm& comm, const MPI_Comm& interComm, int tag) :
comm(comm), interComm(interComm), tag(tag)
{
MPI_Comm_rank(comm, &rank);
}
void waitPrevSend()
{
printf("[work %d] waiting for previous message\n", tag);
MPI_Wait(&sizeReq, MPI_STATUS_IGNORE);
MPI_Wait(&dataReq, MPI_STATUS_IGNORE);
sizeReq = MPI_REQUEST_NULL;
dataReq = MPI_REQUEST_NULL;
}
void workAndSend()
{
waitPrevSend();
printf("[work %d] creating data\n", tag);
data.resize(tag + 42, tag);
sizeInBytes = data.size();
MPI_Issend(&sizeInBytes, 1, MPI_INT, rank, 2*tag+0, interComm, &sizeReq);
MPI_Issend(data.data(), data.size(), MPI_BYTE, rank, 2*tag+1, interComm, &dataReq);
printf("[work %d] has sent %d bytes of data\n", tag, sizeInBytes);
}
MPI_Request wait()
{
MPI_Request req;
printf("[work %d] posted recv of size\n", tag);
MPI_Irecv(&sizeInBytes, 1, MPI_INT, rank, 2*tag+0, interComm, &req);
return req;
}
void recv()
{
data.resize(sizeInBytes);
MPI_Recv(data.data(), data.size(), MPI_BYTE, rank, 2*tag+1, interComm, MPI_STATUS_IGNORE);
printf("[work %d] has recved %d bytes of data\n", tag, sizeInBytes);
}
MPI_Comm comm, interComm;
int rank;
int tag;
MPI_Request sizeReq {MPI_REQUEST_NULL}, dataReq {MPI_REQUEST_NULL};
std::vector<char> data;
int sizeInBytes;
};
class Master
{
public:
Master(const MPI_Comm& comm, const MPI_Comm& interComm) :
comm(comm), interComm(interComm)
{
MPI_Comm_rank(comm, &rank);
}
void run(std::vector<Work>& work, int niter)
{
for (int i = 0; i < niter; ++i)
for (auto& w : work)
w.workAndSend();
sendEndMsg();
}
void sendEndMsg()
{
MPI_Ssend(&endMsg, 1, MPI_INT, rank, endTag, interComm);
}
MPI_Comm comm, interComm;
int rank;
};
class Slave
{
public:
Slave(const MPI_Comm& comm, const MPI_Comm& interComm) :
comm(comm), interComm(interComm)
{
MPI_Comm_rank(comm, &rank);
}
void run(std::vector<Work>& work)
{
std::vector<MPI_Request> reqs;
for (auto& w : work)
reqs.push_back(w.wait());
reqs.push_back(recvEndMsg());
while (true)
{
int id;
MPI_Status status;
printf("waiting for one of %d requests to complete\n", (int) reqs.size());
MPI_Waitany(reqs.size(), reqs.data(), &id, &status);
if (id == (int) reqs.size() - 1)
{
for (auto& req : reqs)
{
if (req != MPI_REQUEST_NULL)
{
MPI_Cancel(&req);
MPI_Request_free(&req);
}
}
return;
}
else
{
work[id].recv();
reqs[id] = work[id].wait();
}
}
}
MPI_Request recvEndMsg()
{
MPI_Request req;
int msg;
MPI_Irecv(&msg, 1, MPI_INT, rank, endTag, interComm, &req);
return req;
}
MPI_Comm comm, interComm;
int rank;
};
int main(int argc, char **argv)
{
MPI_Init(&argc, &argv);
int rank;
int size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if ((size%2) != 0)
MPI_Abort(MPI_COMM_WORLD, 1);
MPI_Comm teamComm, interComm;
int team = rank % 2;
MPI_Comm_split(MPI_COMM_WORLD, team, rank, &teamComm);
const int localLeader = 0;
const int remoteLeader = team ? 0 : 1;
const int tag = 42;
const int nwork = 1;
MPI_Intercomm_create(teamComm, localLeader, MPI_COMM_WORLD, remoteLeader, tag, &interComm);
std::vector<Work> work;
for (int i = 0; i < nwork; ++i)
work.emplace_back(Work(teamComm, interComm, i));
if (team == 0)
{
Master master(teamComm, interComm);
master.run(work, 10);
}
else
{
Slave slave(teamComm, interComm);
slave.run(work);
}
MPI_Comm_free(&interComm);
MPI_Comm_free(&teamComm);
MPI_Finalize();
return 0;
}
*
1008 Запуск *
mpirun -n 2 -l ./test_intercomm
приводит к тупику только в mpich-3.3. Есть идеи?
EDIT:
Я также попытался уменьшить тег остановки до чего-то меньшего, как это предлагалось, того же поведения.
Вывод вышеуказанной команды:
[0] [work 0] waiting for previous message
[0] [work 0] creating data
[0] [work 0] has sent 42 bytes of data
[1] [work 0] posted recv of size
[1] waiting for one of 2 requests to complete
[0] [work 0] waiting for previous message
Таким образом, блокировка ранга 1 в waitany и блокировка ранга 0 при ожидании завершения запроса на отправку (вторые - фактические данные, чье recv публикуется только на ранг 1 после того, как оно прошло waitany).
Мне кажется, что MPI_Waitany
блокирует все.