MPI-передача неизвестного подкласса с boost :: mpi :: pack_oarchive и pack_iarchive

Я пытаюсь передать класс неизвестного подкласса, но известного базового класса.

Я считаю, что это должно быть возможно с помощью boost::serialization, BOOST_CLASS_EXPORT_GUID а также 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?

1

Решение

Вы довольно близки: во-первых, вам не хватает реальных коммуникационных функций для упакованных архивов. Вы пытаетесь извлечь из packed_iarchive не получив ничего в этот архив. Например, вы можете использовать broadcast как описано:

с упакованными архивами корень отправляет pack_oarchive […], тогда как другие процессы получают acked_iarchive […].

Однако простое добавление вызовов к вашему примеру создаст Action вместо HelloAction, Это займет немного больше усилий:

  • Использование указателей базовых классов не-виртуальных классов не очень хорошая идея. Это может работать в вашем случае, потому что подклассы — не что иное, как разные конструкторы, но это становится проблематичным для чуть более сложных примеров.
  • Boost ожидает, что вызовы (де) -сериализации будут использовать симметричные типы. Вы не можете сериализовать HelloAction* а затем десериализовать Action*независимо от того, виртуальный ли это класс1. Если вы должны использовать Action* в обоих случаях вам, конечно, также нужно использовать виртуальные классы, так что boost действительно знает тип среды выполнения. Технически вы можете сериализовать Action* к HelloAction Объект, а затем десериализовать Action* что дает нарезанный 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 Я не могу найти определенное утверждение об этом в документации. Это основано на дела идут ужасно неправильно, если вы попробуете иначе. Буду признателен за комментарий / лучший ответ по этому поводу.

1

Другие решения

Других решений пока нет …