【问题标题】:How to prevent calling of MPI_Finalize() within class destructor?如何防止在类析构函数中调用 MPI_Finalize()?
【发布时间】: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&amp; comm).
  • 另一种选择是使用MPI_Finalized()保护析构函数的MPI调用
  • @VictorEijkhout 我真的认为最后一个建议可以解决问题,但事实并非如此。即使我们将通信器的引用传递给类的构造函数,如果有MPI_Type_free(&amp;M_INT) 行,析构函数仍会调用MPI_Finalize()

标签: c++ mpi


【解决方案1】:

@VictorEijkhout 在他的 cmets 中建议了以下解决方案。想法是创建一个通信器对象,在其构造函数和析构函数中调用MPI_InitMPI_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(&argc, &argv);
    }
    ~MPI_Obj()
    {
        MPI_Finalized(&already_finalised);
        if (!already_finalised)
            MPI_Finalize();
    }
};

class foo
{
public:
    MPI_Datatype M_INT;

    foo(const MPI_Obj& mpi) 
    {
        MPI_Comm_rank(mpi.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);
    }
private:
    int id;
};

int main(int argc, char* argv[])
{
    MPI_Obj comm_std(argc, argv);
    foo bar(comm_std);
    
    return 0;
}

【讨论】:

    猜你喜欢
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    • 2014-05-21
    • 2021-11-26
    • 1970-01-01
    • 1970-01-01
    • 2016-08-04
    相关资源
    最近更新 更多