使用boost :: mpi :: packed_oarchive和packed_iarchive传输未知子类的MPI

时间:2016-03-30 11:52:43

标签: c++ mpi boost-serialization boost-mpi

我正在尝试传输一类未知的子类,但已知基类。

我相信这应该可以使用boost::serializationBOOST_CLASS_EXPORT_GUIDboost::mpi,但我对C ++一般都是新手

这是我的代码:

#include <boost/mpi.hpp>
#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>

namespace mpi = boost::mpi;


class Action {
protected:
    int start_rank;
    std::string greeting;

    Action(std::string greeting) {
        mpi::communicator world;
        this->start_rank = world.rank();
        this->greeting = greeting;
    };

private:

    friend class boost::serialization::access;
    template<class Archive> void serialize(Archive &ar, const unsigned int version) {
        ar & this->start_rank;
        ar & this->greeting;
    };

public:

    Action() = default;

    void invoke() {
        mpi::communicator world;
        std::cout << this->greeting << "! I am process " << world.rank() << " of " << world.size()
            << ". I was created on " << this->start_rank << "." << std::endl;
    };
};


class HelloAction : public Action {

public:
    HelloAction() : Action("Hello") {};

};

class GoodByeAction : public Action {

public:
    GoodByeAction() : Action("Good bye") {};

};

BOOST_CLASS_EXPORT_GUID(Action, "Action");
BOOST_CLASS_EXPORT_GUID(HelloAction, "HelloAction");
BOOST_CLASS_EXPORT_GUID(GoodByeAction, "GoodByeAction");

int main() {
    mpi::environment env;
    mpi::communicator world;

    HelloAction *hello = new HelloAction();
    mpi::broadcast(world, hello, 0);
    hello->invoke();

    GoodByeAction *bye = new GoodByeAction();
    mpi::broadcast(world, bye, 1);
    bye->invoke();

    world.barrier();

    if (world.rank() == 0) {
        std::cout << "sending unknown action classes!" << std::endl;
        HelloAction *yup = new HelloAction();
        boost::mpi::packed_oarchive oar(world);
        oar << yup;
    }
    else {
        std::cout << "receiving unknown action classes!" << std::endl;
        Action *action = NULL;
        boost::mpi::packed_iarchive iar(world);
        iar >> action;
        action->invoke();
    }

    return 0;
}

编译/运行:

mpic++ -g -std=c++1y hello.cpp -lboost_serialization -lmpi -lboost_mpi
mpiexec -np 2 ./a.out

这似乎运行得很好:

Hello! I am process 0 of 2. I was created on 0.
Hello! I am process 1 of 2. I was created on 0.
Good bye! I am process 1 of 2. I was created on 1.
Good bye! I am process 0 of 2. I was created on 1.

...直到“发送/接收未知的动作类”,我得到运行时错误:

receiving unknown action classes!
sending unknown action classes!
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::mpi::exception> >'
  what():  MPI_Unpack: MPI_ERR_ARG: invalid argument of some other kind
[machine-name:20194] *** Process received signal ***
[machine-name:20194] Signal: Aborted (6)
[machine-name:20194] Signal code:  (-6)
[machine-name:20194] [ 0] /lib/x86_64-linux-gnu/libc.so.6(+0x352f0) [0x7fa685cc22f0]
[machine-name:20194] [ 1] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x37) [0x7fa685cc2267]
[machine-name:20194] [ 2] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a) [0x7fa685cc3eca]
[machine-name:20194] [ 3] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(_ZN9__gnu_cxx27__verbose_terminate_handlerEv+0x16d) [0x7fa6862fdb7d]
[machine-name:20194] [ 4] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8d9c6) [0x7fa6862fb9c6]
[machine-name:20194] [ 5] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8da11) [0x7fa6862fba11]
[machine-name:20194] [ 6] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8dc29) [0x7fa6862fbc29]
[machine-name:20194] [ 7] ./a.out(_ZN5boost15throw_exceptionINS_3mpi9exceptionEEEvRKT_+0x80) [0x41426d]
[machine-name:20194] [ 8] ./a.out() [0x413802]
[machine-name:20194] [15] ./a.out() [0x4306e6]
[machine-name:20194] [16] /usr/lib/x86_64-linux-gnu/libboost_serialization.so.1.58.0(_ZN5boost7archive6detail19basic_iarchive_impl12load_pointerERNS1_14basic_iarchiveERPvPKNS1_25basic_pointer_iserializerEPFS9_RKNS_13serialization18extended_type_infoEE+0x4d) [0x7fa686df5b8d]
[machine-name:20194] [17] ./a.out() [0x41d08d]
[machine-name:20194] [23] ./a.out() [0x40d293]
[machine-name:20194] [24] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7fa685cada40]
[machine-name:20194] [25] ./a.out() [0x40cfc9]
[machine-name:20194] *** End of error message ***
--------------------------------------------------------------------------
mpiexec noticed that process rank 1 with PID 20194 on node machine-name exited on signal 6 (Aborted).
--------------------------------------------------------------------------

问题:

  1. 虽然这个问题是用boost标记的,但是有没有可能在有或没有提升的情况下传输一个未知的子类?我知道我可以通过字符串名称进行排序注册,以及控制人员的反转以处理创建,但我认为这是BOOST_CLASS_EXPORT_GUID的意图。
  2. 有没有办法让我的工作与我的工作相结合?
  3. 是否有一个相当容易的替代方案(不使用boost)来使用常规的'MPI?

1 个答案:

答案 0 :(得分:1)

您非常接近:首先,您缺少打包档案的实际通信功能。您试图从packed_iarchive中提取而未收到任何内容。例如,您可以将broadcast用作described

  

使用压缩存档,root发送packed_oarchive [...]而其他进程收到acked_iarchive [...]。

但是,只需添加对您示例的调用即可实例化Action而不是HelloAction。需要更多的努力:

  • 使用非虚拟类的基类指针不是一个好主意。它可能适用于您的情况,因为子类只不过是不同的构造函数,但对于稍微复杂的示例而言它会成为问题。
  • Boost期望(de)-serialization调用使用对称类型。您无法序列化HelloAction*然后反序列化Action*,无论它是否为虚拟类 1 。如果在这两种情况下都必须使用Action*,那么当然也必须使用虚拟类,因此boost实际上知道运行时类型。从技术上讲,您可以将Action*序列化为HelloAction对象,然后反序列化Action* Action - 原始版本的serialize #include <boost/mpi.hpp> #include <boost/mpi/environment.hpp> #include <boost/mpi/communicator.hpp> #include <boost/serialization/string.hpp> #include <boost/serialization/serialization.hpp> #include <boost/serialization/export.hpp> #include <boost/serialization/vector.hpp> #include <iostream> namespace mpi = boost::mpi; class Action { protected: int start_rank; std::string greeting; Action(std::string greeting) { mpi::communicator world; std::cout << world.rank() << ": Creating a Action: " << greeting << std::endl; this->start_rank = world.rank(); this->greeting = greeting; }; private: friend class boost::serialization::access; template<class Archive> void serialize(Archive &ar, const unsigned int version) { ar & this->start_rank; ar & this->greeting; }; public: virtual ~Action() = default; Action() { mpi::communicator world; std::cout << world.rank() << ": Creating a naked Action" << std::endl; } void invoke() { mpi::communicator world; std::cout << this->greeting << "! I am process " << world.rank() << " of " << world.size() << ". I was created on " << this->start_rank << "." << std::endl; }; }; class HelloAction : public Action { public: HelloAction() : Action("Hello") { mpi::communicator world; std::cout << world.rank() << ": Creating an HelloAction" << std::endl; } template<typename Archive> void serialize(Archive & ar, const unsigned int file_version) { ar & boost::serialization::base_object<Action>(*this); } }; class GoodByeAction : public Action { public: GoodByeAction() : Action("Good bye") { mpi::communicator world; std::cout << world.rank() << ": Creating an GoodByeAction" << std::endl; } template<typename Archive> void serialize(Archive & ar, const unsigned int file_version) { ar & boost::serialization::base_object<Action>(*this); } }; // It is totally fine to use the EXPORT (without GUID) for MPI BOOST_CLASS_EXPORT(GoodByeAction) BOOST_CLASS_EXPORT(HelloAction) int main() { mpi::environment env; mpi::communicator world; HelloAction *hello = new HelloAction(); mpi::broadcast(world, hello, 0); hello->invoke(); GoodByeAction *bye = new GoodByeAction(); mpi::broadcast(world, bye, 1); bye->invoke(); world.barrier(); if (world.rank() == 0) { std::cout << "sending unknown action classes!" << std::endl; Action *yup = new HelloAction(); boost::mpi::packed_oarchive oar(world); oar << yup; mpi::broadcast(world, oar, 0); } else { std::cout << "receiving unknown action classes!" << std::endl; boost::mpi::packed_iarchive iar(world); mpi::broadcast(world, iar, 0); Action *action = nullptr; iar >> action; std::cout << "RTTI: " << typeid(*action).name() << std::endl; action->invoke(); } return 0; } 部分。同样,这适用于您的示例,但它不适用于更复杂的示例,因此不是一个好主意。
  • 您必须为子类专门设置fullpage.js以及 1

将所有内容放在一起的工作示例如下所示: 注意:我添加了一些输出以验证是否使用正确的类型创建了对象。

Fullpage.js

关于普通的旧MPI:听起来像是重新发明轮子 - 当然可能但不一定是可取的。

1 我在文档中找不到关于它的明确声明。这是基于如果你尝试其他方式可能会出现严重错误。我很感激有关此问题的评论/更好的答案。