未知子 class 与 boost::mpi::packed_oarchive 和 packed_iarchive 的 MPI 传输
MPI transmission of unknown sub-class with boost::mpi::packed_oarchive and packed_iarchive
我正在尝试传输一个 class 的未知子 class,但已知基 class。
我相信这应该可以使用 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;
}
compiling/running 与:
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.
... 直到到达 "sending/receiving of unknown action classes",在那里我得到 运行 时间错误:
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).
--------------------------------------------------------------------------
问题:
- 尽管这个问题被标记为
boost
,是否有可能在有或没有增强的情况下传输一个未知的 subclass?我知道我可以按字符串名称进行各种注册,以及一个控制反转的人来处理创建,但我认为这是 BOOST_CLASS_EXPORT_GUID
. 的意图
- 有什么方法可以让我用我现有的东西工作吗?
- 是否有一个相当简单的替代方案(不使用
boost
)使它与常规的 ol' MPI 一起工作?
您非常接近:首先,您缺少压缩档案的实际通信功能。您正在尝试从 packed_iarchive
中提取内容,但未将任何内容接收到该存档中。例如,您可以将 broadcast
用作 described:
with packed archives, the root sends a packed_oarchive [...] whereas the other processes receive a acked_iarchive [...].
但是,只需将调用添加到您的示例中,就会实例化一个 Action
而不是 HelloAction
。需要更多的努力:
- 使用非虚拟 classes 的基 class 指针不是一个好主意。它可能适用于您的情况,因为 subclasses 只不过是不同的构造函数,但对于稍微复杂的示例来说它会成为问题。
- Boost 期望(反)序列化调用使用对称类型。您不能序列化
HelloAction*
然后反序列化 Action*
,无论它是否是虚拟 class1。如果你在这两种情况下都必须使用Action*
,你当然也必须使用virtual classes,这样boost实际上知道运行时类型。从技术上讲,您可以将一个 Action*
序列化为一个 HelloAction
对象,然后反序列化一个 Action*
以生成原始对象的 sliced Action
部分。同样,这适用于您的示例,但它不适用于更复杂的示例,因此不是一个好主意。
- 您还必须为子 class 专精
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 我在文档中找不到关于此的明确声明。这是基于如果你不这样尝试,事情就会变得非常糟糕。我将不胜感激对此发表评论/更好的回答。
我正在尝试传输一个 class 的未知子 class,但已知基 class。
我相信这应该可以使用 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;
}
compiling/running 与:
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.
... 直到到达 "sending/receiving of unknown action classes",在那里我得到 运行 时间错误:
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).
--------------------------------------------------------------------------
问题:
- 尽管这个问题被标记为
boost
,是否有可能在有或没有增强的情况下传输一个未知的 subclass?我知道我可以按字符串名称进行各种注册,以及一个控制反转的人来处理创建,但我认为这是BOOST_CLASS_EXPORT_GUID
. 的意图
- 有什么方法可以让我用我现有的东西工作吗?
- 是否有一个相当简单的替代方案(不使用
boost
)使它与常规的 ol' MPI 一起工作?
您非常接近:首先,您缺少压缩档案的实际通信功能。您正在尝试从 packed_iarchive
中提取内容,但未将任何内容接收到该存档中。例如,您可以将 broadcast
用作 described:
with packed archives, the root sends a packed_oarchive [...] whereas the other processes receive a acked_iarchive [...].
但是,只需将调用添加到您的示例中,就会实例化一个 Action
而不是 HelloAction
。需要更多的努力:
- 使用非虚拟 classes 的基 class 指针不是一个好主意。它可能适用于您的情况,因为 subclasses 只不过是不同的构造函数,但对于稍微复杂的示例来说它会成为问题。
- Boost 期望(反)序列化调用使用对称类型。您不能序列化
HelloAction*
然后反序列化Action*
,无论它是否是虚拟 class1。如果你在这两种情况下都必须使用Action*
,你当然也必须使用virtual classes,这样boost实际上知道运行时类型。从技术上讲,您可以将一个Action*
序列化为一个HelloAction
对象,然后反序列化一个Action*
以生成原始对象的 slicedAction
部分。同样,这适用于您的示例,但它不适用于更复杂的示例,因此不是一个好主意。 - 您还必须为子 class 专精
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 我在文档中找不到关于此的明确声明。这是基于如果你不这样尝试,事情就会变得非常糟糕。我将不胜感激对此发表评论/更好的回答。