#c #mpi
Вопрос:
У меня есть класс, который создает новый MPI_Datatype
в своем конструкторе, а затем удаляет его в своем деструкторе. Однако наличие удаления пользовательского типа данных каким-то образом вызывает вызов MPI_Finalize()
внутри деструктора.
#include <cstdio>
#include "mpi.h"
class foo
{
public:
MPI_Datatype M_INT;
foo(MPI_Comm comm)
{
MPI_Comm_rank(comm, amp;id);
printf("-: Constructing foo.n", id);
MPI_Type_dup(MPI_INT, amp;M_INT);
}
~foo()
{
printf("-: Destructing foo.n", id);
MPI_Type_free(amp;M_INT); // problematic line
}
private:
int id;
};
int main(int argc, char* argv[])
{
MPI_Init(amp;argc, amp;argv);
foo bar(MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
Это приводит к ошибке во время выполнения, которая вызывает жалобы Attempting to use an MPI routine after finalizing MPICH
. Удаление удаления пользовательского MPI_Datatype
элемента в деструкторе является временным обходным решением. Тем не менее, я хотел бы лучшего, правильного решения. Если вы знаете, почему это происходит и как это решить, пожалуйста, объясните. Спасибо.
Редактировать:
В соответствии с предложениями в комментариях, я попробовал следующее. Это все еще та же проблема, что и в моем предыдущем примере кода.
#include <cstdio>
#include "mpi.h"
class M_Comm
{
public:
MPI_Comm p;
int already_finalised;
M_Comm(const MPI_Commamp; _comm) : p(_comm) {}
~M_Comm()
{
MPI_Finalized(amp;already_finalised);
if (!already_finalised)
MPI_Finalize();
}
};
class foo
{
public:
MPI_Datatype M_INT;
foo(const M_Commamp; comm)
{
MPI_Comm_rank(comm.p, amp;id);
printf("-: Constructing foo.n", id);
MPI_Type_dup(MPI_INT, amp;M_INT);
}
~foo()
{
printf("-: Destructing foo.n", id);
MPI_Type_free(amp;M_INT); // problematic line
}
private:
int id;
};
int main(int argc, char* argv[])
{
MPI_Init(amp;argc, amp;argv);
M_Comm comm_std(MPI_COMM_WORLD);
foo bar(comm_std);
MPI_Finalize();
return 0;
}
Комментарии:
1. Я предполагаю, что деструктор вызывается при выходе из области (например, после
return 0
). Попробуйте поместить свой тип в его собственную область (например{foo bar(MPI_COMM_WORLD);}
) и посмотрите, делает ли компилятор то, что вы ожидаете.2. @GillesGouaillardet Да, это еще один обходной путь, но позже мне может понадобиться объект.
3. Вы пытаетесь объединить объектно-ориентированное программирование и прямое императивное программирование. Вам нужно создать объект коммуникатора и сделать
MPI_Finalize
в его деструкторе, который будет вызван в конце вашей программы. (Вы можете получить вдохновение, посмотрев на библиотеки MPI C , такие как MPL.) Кстати:foo(MPI_Comm comm)
это передача по значению. Вы неявно используете тот факт, чтоMPI_Comm
это дескриптор. Если это объект, вы дублируете свой коммуникатор здесь. Использование:foo ( const MPI_Commamp; comm)
.4. другой вариант-защитить вызов MPI деструктора с помощью
MPI_Finalized()
5. @VictorEijkhout Я действительно думал, что последнее предложение может подействовать, но этого не произошло. Даже когда мы передаем ссылку на коммуникатор конструктору класса, деструктор все равно вызывает
MPI_Finalize()
, если естьMPI_Type_free(amp;M_INT)
строка.
Ответ №1:
@VictorEijkhout предложил следующее решение в своих комментариях. Идея состоит в том, чтобы создать объект коммуникатора, который вызывает MPI_Init
и MPI_Finalize
в его конструкторе и деструкторе, а затем передать этот объект другим функциям или классам для дальнейших манипуляций.
#include <cstdio>
#include "mpi.h"
class MPI_Obj
{
public:
MPI_Comm COMM = MPI_COMM_WORLD;
int already_finalised;
MPI_Obj(int argc, char* argv[])
{
MPI_Init(amp;argc, amp;argv);
}
~MPI_Obj()
{
MPI_Finalized(amp;already_finalised);
if (!already_finalised)
MPI_Finalize();
}
};
class foo
{
public:
MPI_Datatype M_INT;
foo(const MPI_Objamp; mpi)
{
MPI_Comm_rank(mpi.COMM, amp;id);
printf("-: Constructing foo.n", id);
MPI_Type_dup(MPI_INT, amp;M_INT);
}
~foo()
{
printf("-: Destructing foo.n", id);
MPI_Type_free(amp;M_INT);
}
private:
int id;
};
int main(int argc, char* argv[])
{
MPI_Obj comm_std(argc, argv);
foo bar(comm_std);
return 0;
}