1

未知のサブクラスのクラスを送信しようとしていますが、既知の基本クラスです。

boost::serializationこれは、 BOOST_CLASS_EXPORT_GUIDandを使用して可能であると信じていますboost::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で機能させるための合理的に簡単な代替手段(を使用しない)はありますか?
4

1 に答える 1

1

あなたはかなり近いです: まず、圧縮されたアーカイブの実際の通信機能がありません。packed_iarchiveそのアーカイブに何も受信せずに から抽出しようとしています。たとえば、次のように使用broadcastできます

パックされたアーカイブでは、ルートはpacked_oarchive [...]を送信しますが、他のプロセスはacked_iarchive [...]を受け取ります。

ただし、例に呼び出しを追加するだけで、 のAction代わりにがインスタンス化されますHelloAction。もう少し手間がかかります:

  • 非仮想クラスの基本クラス ポインターを使用することはお勧めできません。サブクラスは異なるコンストラクターに過ぎないため、あなたの場合はうまくいくかもしれませんが、少し複雑な例では問題になります。
  • Boost は、(de)-serialization 呼び出しが対称型を使用することを想定しています。仮想クラス1HelloAction*であるかどうかに関係なく、 をシリアライズしてからアンシリアライズすることはできません。両方のケースで使用する必要がある場合は、もちろん仮想クラスも使用する必要があるため、boost は実際にランタイム タイプを認識します。技術的には、 をオブジェクトにシリアライズしてから、オリジナルのスライスされた部分を生成する をデシリアライズできます。繰り返しますが、これはあなたの例では機能しますが、より複雑なものでは失敗するため、良い考えではありません.Action*Action*Action*HelloActionAction* Action
  • serializeサブクラスにも特化する必要があります1

すべてをまとめると、実際の例は次のようになります。 注: オブジェクトが正しいタイプで作成されていることを確認するために、もう少し出力を追加しました。

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

単純な古い MPI について: 私には車輪の再発明のように聞こえます - 確かに可能ですが、必ずしも望ましいとは限りません。

1ドキュメントでそれについての明確な声明を見つけることができません。これは、別の方法を試してみると、ひどくうまくいかないことに基づいています。これに関するコメント/より良い回答をいただければ幸いです。

于 2016-03-30T15:30:26.327 に答える