【发布时间】:2021-10-24 18:30:47
【问题描述】:
我有一个类,它在其构造函数中创建一个新的MPI_Datatype,然后在其析构函数中将其删除。然而,自定义数据类型的删除以某种方式触发了在析构函数中调用MPI_Finalize()。
#include <cstdio>
#include "mpi.h"
class foo
{
public:
MPI_Datatype M_INT;
foo(MPI_Comm comm)
{
MPI_Comm_rank(comm, &id);
printf("%2d: Constructing foo.\n", id);
MPI_Type_dup(MPI_INT, &M_INT);
}
~foo()
{
printf("%2d: Destructing foo.\n", id);
MPI_Type_free(&M_INT); // problematic line
}
private:
int id;
};
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
foo bar(MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
这会产生一个运行时错误,抱怨Attempting to use an MPI routine after finalizing MPICH。在析构函数中删除自定义MPI_Datatype 是一种临时解决方法。但是,我想要一个更好、更合适的解决方案。如果您知道为什么会发生这种情况以及如何解决它,请解释一下。谢谢。
编辑:
根据 cmets 中的建议,我尝试了以下方法。这仍然与我之前的示例代码存在完全相同的问题。
#include <cstdio>
#include "mpi.h"
class M_Comm
{
public:
MPI_Comm p;
int already_finalised;
M_Comm(const MPI_Comm& _comm) : p(_comm) {}
~M_Comm()
{
MPI_Finalized(&already_finalised);
if (!already_finalised)
MPI_Finalize();
}
};
class foo
{
public:
MPI_Datatype M_INT;
foo(const M_Comm& comm)
{
MPI_Comm_rank(comm.p, &id);
printf("%2d: Constructing foo.\n", id);
MPI_Type_dup(MPI_INT, &M_INT);
}
~foo()
{
printf("%2d: Destructing foo.\n", id);
MPI_Type_free(&M_INT); // problematic line
}
private:
int id;
};
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
M_Comm comm_std(MPI_COMM_WORLD);
foo bar(comm_std);
MPI_Finalize();
return 0;
}
【问题讨论】:
-
我猜当你退出作用域时会调用析构函数(例如在
return 0之后)。尝试将您的类型放在自己的范围内(例如{foo bar(MPI_COMM_WORLD);}),看看编译器是否符合您的预期。 -
@GillesGouaillardet 是的,这是另一种解决方法,但我以后可能需要该对象。
-
您正在尝试将面向对象编程和直接命令式编程结合起来。您需要创建一个通信器对象,并在其析构函数中执行
MPI_Finalize,这将在您的程序结束时被调用。 (您可以通过查看 C++ MPI 库(例如 MPL)获得灵感。)顺便说一句:foo(MPI_Comm comm)是按值传递的。您隐含地使用MPI_Comm是句柄这一事实。如果它是一个对象,那么您就是在此处复制您的通讯器。使用:foo ( const MPI_Comm& comm). -
另一种选择是使用
MPI_Finalized()保护析构函数的MPI调用 -
@VictorEijkhout 我真的认为最后一个建议可以解决问题,但事实并非如此。即使我们将通信器的引用传递给类的构造函数,如果有
MPI_Type_free(&M_INT)行,析构函数仍会调用MPI_Finalize()。