MPI_SEND - 将结构作为向量发送



考虑这个结构:

struct Book {
int id;
string title;
};

而这个向量:

vector<Book> books;

如何使用MPI_Send来发送矢量books的元素?

我一整天都试图找到一种方法来做到这一点,但没有结果。

方法 1

执行此操作的一种方法是将title设置为恒定长度。然后,您可以围绕结构构建 MPI 数据类型,如下所示:

#include "mpi.h"
#include <iostream>
#include <string>
#include <vector>
const int MAX_TITLE_LENGTH = 256;
struct Book {
int id;
char title[MAX_TITLE_LENGTH];
};
int main(int argc, char *argv[]){
MPI_Init(&argc, &argv);
std::vector<Book> books(343);
MPI_Datatype BookType;
MPI_Datatype type[2] = { MPI_INTEGER, MPI_CHAR };
int blocklen[2] = { 1, MAX_TITLE_LENGTH };
MPI_Aint disp[2];
disp[0] = 0;
disp[1] = sizeof(int);
MPI_Type_create_struct(2, blocklen, disp, type, &BookType);
MPI_Type_commit(&BookType);
int myrank;
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
if (myrank == 0) {
books[3].id = 4;
MPI_Send(books.data(), 343, BookType, 1, 123, MPI_COMM_WORLD);
} else if (myrank == 1) {
MPI_Status status;
MPI_Recv(books.data(), 343, BookType, 0, 123, MPI_COMM_WORLD, &status);
std::cout<<books[3].id<<std::endl;
}
MPI_Finalize();
return 0;
}

方法 2

MPI 最适合用于在已知大小的网格之间快速交换数字。但它也可以作为一个方便的通信层。为此,我们可以使用 Cereal 库序列化任意C++对象,然后使用 MPI 发送序列化表示,如下所示。这比按设计使用 MPI 要慢,因为中间副本更多,但提供了使用C++完全灵活性的灵活性。

#include "mpi.h"
#include <cereal/types/vector.hpp>
#include <cereal/types/string.hpp>
#include <cereal/archives/binary.hpp>
#include <sstream>
#include <string>
struct Book {
int id;
std::string title;
template <class Archive>
void serialize( Archive & ar ) { ar(id,title); }
};
template<class T>
int MPI_Send(const T &data, int dest, int tag, MPI_Comm comm){
std::stringstream ss;
{ //Needed for RAII in Cereal
cereal::BinaryOutputArchive archive( ss );
archive( data );
}
const auto serialized = ss.str();
return MPI_Send(serialized.data(), serialized.size(), MPI_CHAR, dest, tag, MPI_COMM_WORLD);
}
template<class T>
int MPI_Recv(T &data, int source, int tag, MPI_Comm comm, MPI_Status *status){
//Get number of bytes in incoming message
MPI_Probe(source, tag, MPI_COMM_WORLD, status);
int num_incoming;
MPI_Get_count(status, MPI_CHAR, &num_incoming);
//Allocate a buffer of appropriate size
std::vector<char> incoming(num_incoming);
//Receive the data
auto ret = MPI_Recv(incoming.data(), num_incoming, MPI_CHAR, source, tag, MPI_COMM_WORLD, status);
std::stringstream ss;
ss.write(incoming.data(), num_incoming);
//Unpack the data
{
cereal::BinaryInputArchive archive(ss);
archive(data);
}
return ret;
}
int main(int argc, char **argv){
MPI_Init(&argc, &argv);
std::vector<Book> books(343);
int myrank;
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
if (myrank == 0) {
books[3].id    = 4;
books[3].title = "Hello, world!";
MPI_Send(books, 1, 123, MPI_COMM_WORLD);
} else if (myrank == 1){
MPI_Status status;
MPI_Recv(books, 0, 123, MPI_COMM_WORLD, &status);
std::cout<<books[3].id<<" "<<books[3].title<<std::endl;
}
MPI_Finalize();
return 0;
}

由于title是固定大小Nchar[N]数组,因此您可以创建一个新的数据类型并在MPI_Send中使用它。遗憾的是,此方法不适用于作为数据成员的std::string。但是您可以逐个元素发送std::vector<Book>

例如:

std::vector<Book> books;
// ...
const unsigned long long size = books.size();
MPI_Send(&size, 1, MPI_UNSIGNED_LONG_LONG, ...);
for (const auto& book : books) {
MPI_Send(&book.id, 1, MPI_INT, ...);
const unsigned long long len = book.title.length(); 
MPI_Send(&len, 1, MPI_UNSIGNED_LONG_LONG, ...);
MPI_Send(book.title.data(), len, MPI_CHAR, ...);
}

std::vector<Book> books;
unsigned long long size;
MPI_Recv(&size, 1, MPI_UNSIGNED_LONG_LONG, ...);
books.resize(size);
for (auto& book : books) {    
MPI_Recv(&book.id, 1, MPI_INT, ...);
unsigned long long len;
MPI_Recv(&len, 1, MPI_UNSIGNED_LONG_LONG, ...);
std::vector<char> str(len);
MPI_Recv(str.data(), len, MPI_CHAR, ...);
book.title.assign(str.begin(), str.end());
}
// ...

最新更新