如何在MPI_Send中发送设置对象

时间:2015-06-23 21:41:35

标签: c++ set mpi mpic++

我搜索发送了一个设置对象,我找到的最接近的是矢量(它不同,不适用于套装)。

如何在MPI_Send中发送设置对象? (不使用boost库)任何人都可以举一个简单的例子吗?

1 个答案:

答案 0 :(得分:6)

您是否必须在MPI中将复杂数据结构写入文件或网络,问题是相同的;您必须将数据提取到“普通旧数据”(POD),保存,然后输出,同样能够将保存的数据解压缩到相同类型的结构中。通常,这称为序列化。

对于任何给定的结构,您总是可以编写自己的例程来执行此操作,但在C ++中,Boost中有一个名为Boost Serialization Library的框架用于执行此操作;它对于这个例子来说有点重量级,但它适用于大多数(所有?)STL容器,并且有一些钩子可以为你自己的类添加支持。

使用Boost的主要技巧是Boost库(以及所有示例)使得将数据写入文件非常容易,但是在这里你想把它保存在内存中并通过它发送/接收它网络;这意味着跳过更多的箍,以确保序列化进入您可以访问的数组。 This SO answer在这方面非常有帮助。

所以一个完整的工作示例如下所示:

#include <mpi.h>
#include <set>
#include <string>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/set.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/back_inserter.hpp>

int main(int argc,char** argv) {

    int size, rank;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (size < 2) {
        if (rank == 0)
            std::cerr << "Require at least 2 tasks" << std::endl;
        MPI_Abort(MPI_COMM_WORLD, 1);
    }

   const int lentag=0;
   const int datatag=1;
   if (rank == 0) {
        int nums[] = {1,4,9,16};
        std::set<int> send_set(nums, nums+4);

        std::cout << "Rank " << rank << " sending set: ";
        for (std::set<int>::iterator i=send_set.begin(); i!=send_set.end(); i++)
            std::cout << *i << " ";
        std::cout << std::endl;

        // We're going to serialize into a std::string of bytes, and then send this
        std::string serial_str;
        boost::iostreams::back_insert_device<std::string> inserter(serial_str);
        boost::iostreams::stream<boost::iostreams::back_insert_device<std::string> > s(inserter);
        boost::archive::binary_oarchive send_ar(s);

        send_ar << send_set;
        s.flush();
        int len = serial_str.size();

        // Send length, then data
        MPI_Send( &len, 1, MPI_INT, 1, lentag, MPI_COMM_WORLD );
        MPI_Send( (void *)serial_str.data(), len, MPI_BYTE, 1, datatag, MPI_COMM_WORLD );
    } else if (rank == 1) {
        int len;
        MPI_Recv( &len, 1, MPI_INT, 0, lentag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

        char data[len+1];
        MPI_Recv( data, len, MPI_BYTE, 0, datatag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        data[len] = '\0';

        boost::iostreams::basic_array_source<char> device(data, len);
        boost::iostreams::stream<boost::iostreams::basic_array_source<char> > s(device);
        boost::archive::binary_iarchive recv_ar(s);

        std::set<int> recv_set;
        recv_ar >> recv_set;

        std::cout << "Rank " << rank << " got set: ";
        for (std::set<int>::iterator i=recv_set.begin(); i!=recv_set.end(); i++)
            std::cout << *i << " ";
        std::cout << std::endl;
    }

    MPI_Finalize();
    return 0;
}

跑步给出:

$ mpic++ mpi-set.cxx -o mpiset -lboost_serialization
$ mpirun -np 2 ./mpiset
Rank 0 sending set: 1 4 9 16
Rank 1 got set: 1 4 9 16

如果您真的不想使用Boost,因为您实际上无法直接看到设置数据结构,除了将数据提取到数组或向量中之外没有太多选择,并以这种方式发送数据:

#include <mpi.h>
#include <set>
#include <vector>

int main(int argc,char** argv) {

    int size, rank;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (size < 2) {
        if (rank == 0)
            std::cerr << "Require at least 2 tasks" << std::endl;
        MPI_Abort(MPI_COMM_WORLD, 1);
    }

   const int lentag=0;
   const int datatag=1;
   if (rank == 0) {
        int nums[] = {1,4,9,16};
        std::set<int> send_set(nums, nums+4);

        std::cout << "Rank " << rank << " sending set: ";
        for (std::set<int>::iterator i=send_set.begin(); i!=send_set.end(); i++)
            std::cout << *i << " ";
        std::cout << std::endl;

        // Send length, then data
        int len = send_set.size();
        MPI_Send( &len, 1, MPI_INT, 1, lentag, MPI_COMM_WORLD );

        std::vector<int> send_vec;
        for (std::set<int>::iterator i=send_set.begin(); i!=send_set.end(); i++) 
            send_vec.push_back(*i);

        MPI_Send( send_vec.data(), len, MPI_INT, 1, datatag, MPI_COMM_WORLD );

    } else if (rank == 1) {

        int len;
        MPI_Recv( &len, 1, MPI_INT, 0, lentag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

        int recv_data[len];
        MPI_Recv( recv_data, len, MPI_INT, 0, datatag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

        std::set<int> recv_set;
        for (int i=0; i<len; i++) 
            recv_set.insert(recv_data[i]);

        std::cout << "Rank " << rank << " got set: ";
        for (std::set<int>::iterator i=recv_set.begin(); i!=recv_set.end(); i++)
            std::cout << *i << " ";
        std::cout << std::endl;
    }

    MPI_Finalize();
    return 0;
}

跑步给出:

$ mpicxx -o mpisetvector mpi-set-vector.cxx 
$ mpirun -np 2 mpisetvector
Rank 0 sending set: 1 4 9 16 
Rank 1 got set: 1 4 9 16 

但实际上,如果你打算用其他类型的对象做这件事,那么Boost就是你的选择。