Проблема типа данных, полученная из MPI, вызванная заполнением структуры и проблемой неблокирующего буфера связи - PullRequest
1 голос
/ 21 марта 2020

Здравствуйте, я пишу программу c++, в которой я хочу, чтобы MPI взаимодействовал по производному типу данных. Но получатель не получает полную информацию, которую отправляет отправитель.

Вот как я создаю свой производный тип данных:

// dg_derived_datatype.cpp

#include <mpi.h>
#include "dg_derived_datatype.h"

namespace Hash{

    MPI_Datatype Face_type;
};

void Construct_data_type(){

    MPI_Face_type();

}

void MPI_Face_type(){

    int num = 3;

    // Number of elements in each block (array of integers)
    int elem_blocklength[num]{2, 1, 5};

    // Byte displacement of each block (array of integers).
    MPI_Aint array_of_offsets[num];
    MPI_Aint intex, charex;
    MPI_Aint lb;
    MPI_Type_get_extent(MPI_INT, &lb, &intex);
    MPI_Type_get_extent(MPI_CHAR, &lb, &charex);

    array_of_offsets[0] = (MPI_Aint) 0;
    array_of_offsets[1] = array_of_offsets[0] + intex * 2;
    array_of_offsets[2] = array_of_offsets[1] + charex;

    MPI_Datatype array_of_types[num]{MPI_INT, MPI_CHAR, MPI_INT};

    // create and MPI datatype
    MPI_Type_create_struct(num, elem_blocklength, array_of_offsets, array_of_types, &Hash::Face_type);  
    MPI_Type_commit(&Hash::Face_type);

}

void Free_type(){

    MPI_Type_free(&Hash::Face_type);    

}

Здесь я получаю свой тип данных Hash::Face_type и фиксирую его. Hash::Face_type используется для передачи вектора моей структуры (face_pack, 2 int + 1 char + 5 int).

// dg_derived_datatype.h

#ifndef DG_DERIVED_DATA_TYPE_H
#define DG_DERIVED_DATA_TYPE_H

#include <mpi.h>

struct face_pack{

    int owners_key; 

    int facei; 

    char face_type;

    int hlevel;

    int porderx;

    int pordery; 

    int key;

    int rank;

};

namespace Hash{

    extern MPI_Datatype Face_type;
};

void Construct_data_type();

void Free_type();

#endif

Затем в своей основной программе я делаю

// dg_main.cpp

#include <iostream>
#include <mpi.h>
#include "dg_derived_datatype.h"
#include <vector>

void Recv_face(int source, int tag, std::vector<face_pack>& recv_face);

int main(){
// Initialize MPI. 
// some code here.
// I create a vector of struct: std::vector<face_pack> face_info,
// to store the info I want to let proccesors communicate. 

Construct_data_type(); // construct my derived data type

MPI_Request request_pre1, request_pre2, request_next1, request_next2;

// send
if(num_next > 0){ // If fullfilled the current processor send info to the next processor (myrank + 1)

std::vector<face_pack> face_info;
// some code to construct face_info

// source my_rank, destination my_rank + 1
MPI_Isend(&face_info[0], num_n, Hash::Face_type, mpi::rank + 1, mpi::rank + 1, MPI_COMM_WORLD, &request_next2);

}

// recv
if(some critira){ // recv from the former processor (my_rank - 1)

std::vector<face_pack> recv_face;

Recv_face(mpi::rank - 1, mpi::rank, recv_face); // recv info from former processor

}
if(num_next > 0){

        MPI_Status status;
        MPI_Wait(&request_next2, &status);

}

Free_type();

// finialize MPI
}

void Recv_face(int source, int tag, std::vector<face_pack>& recv_face){

    MPI_Status status1, status2;

    MPI_Probe(source, tag, MPI_COMM_WORLD, &status1);

    int count;
    MPI_Get_count(&status1, Hash::Face_type, &count);

    recv_face = std::vector<face_pack>(count);

    MPI_Recv(&recv_face[0], count, Hash::Face_type, source, tag, MPI_COMM_WORLD, &status2);
}


Проблема в том, что получатель иногда получает неполную информацию.

Например, я распечатываю face_info до его отправки:

// rank 2
owners_key3658 facei 0 face_type M neighbour 192 n_rank 0
owners_key3658 facei 1 face_type L neighbour 66070 n_rank 1
owners_key3658 facei 1 face_type L neighbour 76640 n_rank 1
owners_key3658 facei 2 face_type M neighbour 2631 n_rank 0
owners_key3658 facei 3 face_type L neighbour 4953 n_rank 1
...
owners_key49144 facei 1 face_type M neighbour 844354 n_rank 2
owners_key49144 facei 1 face_type M neighbour 913280 n_rank 2
owners_key49144 facei 2 face_type L neighbour 41619 n_rank 1
owners_key49144 facei 3 face_type M neighbour 57633 n_rank 2

Что правильно.

Но на стороне получателя я распечатываю полученное сообщение:

owners_key3658 facei 0 face_type M neighbour 192 n_rank 0
owners_key3658 facei 1 face_type L neighbour 66070 n_rank 1
owners_key3658 facei 1 face_type L neighbour 76640 n_rank 1
owners_key3658 facei 2 face_type M neighbour 2631 n_rank 0
owners_key3658 facei 3 face_type L neighbour 4953 n_rank 1

... // at the beginning it's fine, however, at the end it messed up

owners_key242560 facei 2 face_type ! neighbour 2 n_rank 2
owners_key217474 facei 2 face_type ! neighbour 2 n_rank 2
owners_key17394 facei 2 face_type ! neighbour 2 n_rank 2
owners_key216815 facei 2 face_type ! neighbour 2 n_rank 2

Конечно, он потерял информацию face_type, которая является символом. И, насколько я знаю, std::vector гарантирует непрерывную память здесь . Поэтому я не уверен, какая часть моего производного типа данных MPI неверна. Передача сообщений иногда работает, иногда нет.

1 Ответ

1 голос
/ 24 марта 2020

ОК, я вроде разобрался со своими проблемами. Есть два.

Первый - использование MPI_Type_get_extent(). Поскольку структура c / c ++ может быть дополнена вашим компилятором, вполне нормально, что если вы отправляете только один элемент, но если вы отправляете несколько элементов, завершающий заполнитель может вызвать проблемы (см. Рисунок ниже).

padding

Следовательно, более безопасный и более удобный способ определения вашего производного типа данных - это использовать MPI_Get_address(). Вот как я это делаю:

// generate the derived datatype
void MPI_Face_type(){

    int num = 3;

    int elem_blocklength[num]{2, 1, 5};

    MPI_Datatype array_of_types[num]{MPI_INT, MPI_CHAR, MPI_INT};

    MPI_Aint array_of_offsets[num];
    MPI_Aint baseadd, add1, add2;

    std::vector<face_pack> myface(1);

    MPI_Get_address(&(myface[0].owners_key), &baseadd);
    MPI_Get_address(&(myface[0].face_type), &add1);
    MPI_Get_address(&(myface[0].hlevel), &add2);

    array_of_offsets[0] = 0;
    array_of_offsets[1] = add1 - baseadd;
    array_of_offsets[2] = add2 - baseadd;

    MPI_Type_create_struct(num, elem_blocklength, array_of_offsets, array_of_types, &Hash::Face_type);  

    // check that the extent is correct
    MPI_Aint lb, extent;
    MPI_Type_get_extent(Hash::Face_type, &lb, &extent); 
    if(extent != sizeof(myface[0])){
        MPI_Datatype old = Hash::Face_type;
        MPI_Type_create_resized(old, 0, sizeof(myface[0]), &Hash::Face_type);
        MPI_Type_free(&old);
    }
    MPI_Type_commit(&Hash::Face_type);
}

Второй - использование неблокирующей отправки MPI_Isend(). Программа работает должным образом после того, как я изменил неблокирующую отправку на блокирующую отправку.

Относительная часть моей программы выглядит так:

if(criteria1){

//form the vector using my derived datatype
std::vector<derived_type> my_vector;

// use MPI_Isend to send the vector to the target rank
MPI_Isend(... my_vector...);

}

if(critira2){

// need to recv message 
MPI_Recv();
}

if(critira1){

// the sender now needs to make sure the message has arrived. 
MPI_Wait();
}

Хотя я использовал MPI_Wait, ревертер сделал не получить полное сообщение. Я проверяю справочную страницу MPI_Isend(), там написано man_page

Неблокирующий вызов отправки указывает, что система может начать копировать данные из буфера отправки. Отправитель не должен изменять какую-либо часть буфера отправки после вызова неблокирующей операции отправки до завершения отправки.

Но я не думаю, что я изменил буфер отправки? Или, может быть, в буфере отправки недостаточно места для хранения информации для отправки? В моем понимании, неблокирующая отправка работает следующим образом: отправитель помещает сообщение в свой буфер и отправляет его в целевой ранг, когда целевой ранг достигает MPI_Recv. Так что может быть в буфере отправителя не хватает места для хранения сообщений до того, как он их отправит? Поправь меня, если я ошибаюсь.

...