【问题标题】:MPI_SEND - send structure as vectorMPI_SEND - 将结构作为向量发送
【发布时间】:2020-01-02 18:25:24
【问题描述】:

考虑这个结构:

struct Book {
 int id;
 string title;
};

还有这个向量:

vector<Book> books;

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

我一整天都在想办法做到这一点,但没有结果。

【问题讨论】:

    标签: c++ mpi


    【解决方案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;
    }
    

    方法二

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

    【讨论】:

      【解决方案2】:

      title 是一个固定大小的char[N] 数组N,您可以创建一个新的数据类型并在MPI_Send 中使用它。不幸的是,这种方法不适用于 std::string 作为数据成员。但是您可以逐个发送std::vector&lt;Book&gt;

      例如:

      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());
      }
      // ...
      

      【讨论】:

        猜你喜欢
        • 2016-06-14
        • 1970-01-01
        • 2012-10-23
        • 2014-07-03
        • 1970-01-01
        • 2018-11-09
        • 1970-01-01
        • 2013-08-12
        • 1970-01-01
        相关资源
        最近更新 更多