Как предотвратить вызов функции MPI_Finalize() в деструкторе класса?

#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;
}