// (C) Copyright 2005 Matthias Troyer // (C) Copyright 2006 Douglas Gregor <doug.gregor -at gmail.com> // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // Authors: Matthias Troyer // Douglas Gregor /** @file skeleton_and_content.hpp * * This header provides facilities that allow the structure of data * types (called the "skeleton") to be transmitted and received * separately from the content stored in those data types. These * facilities are useful when the data in a stable data structure * (e.g., a mesh or a graph) will need to be transmitted * repeatedly. In this case, transmitting the skeleton only once * saves both communication effort (it need not be sent again) and * local computation (serialization need only be performed once for * the content). */ #ifndef BOOST_MPI_SKELETON_AND_CONTENT_HPP #define BOOST_MPI_SKELETON_AND_CONTENT_HPP #include <boost/mpi/config.hpp> #include <boost/archive/detail/auto_link_archive.hpp> #include <boost/mpi/packed_iarchive.hpp> #include <boost/mpi/packed_oarchive.hpp> #include <boost/mpi/detail/forward_skeleton_iarchive.hpp> #include <boost/mpi/detail/forward_skeleton_oarchive.hpp> #include <boost/mpi/detail/ignore_iprimitive.hpp> #include <boost/mpi/detail/ignore_oprimitive.hpp> #include <boost/shared_ptr.hpp> #include <boost/archive/detail/register_archive.hpp> namespace boost { namespace mpi { /** * @brief A proxy that requests that the skeleton of an object be * transmitted. * * The @c skeleton_proxy is a lightweight proxy object used to * indicate that the skeleton of an object, not the object itself, * should be transmitted. It can be used with the @c send and @c recv * operations of communicators or the @c broadcast collective. When a * @c skeleton_proxy is sent, Boost.MPI generates a description * containing the structure of the stored object. When that skeleton * is received, the receiving object is reshaped to match the * structure. Once the skeleton of an object as been transmitted, its * @c content can be transmitted separately (often several times) * without changing the structure of the object. */ template <class T> struct BOOST_MPI_DECL skeleton_proxy { /** * Constructs a @c skeleton_proxy that references object @p x. * * @param x the object whose structure will be transmitted or * altered. */ skeleton_proxy(T& x) : object(x) {} T& object; }; /** * @brief Create a skeleton proxy object. * * This routine creates an instance of the skeleton_proxy class. It * will typically be used when calling @c send, @c recv, or @c * broadcast, to indicate that only the skeleton (structure) of an * object should be transmitted and not its contents. * * @param x the object whose structure will be transmitted. * * @returns a skeleton_proxy object referencing @p x */ template <class T> inline const skeleton_proxy<T> skeleton(T& x) { return skeleton_proxy<T>(x); } namespace detail { /// @brief a class holding an MPI datatype /// INTERNAL ONLY /// the type is freed upon destruction class BOOST_MPI_DECL mpi_datatype_holder : public boost::noncopyable { public: mpi_datatype_holder() : is_committed(false) {} mpi_datatype_holder(MPI_Datatype t, bool committed = true) : d(t) , is_committed(committed) {} void commit() { BOOST_MPI_CHECK_RESULT(MPI_Type_commit,(&d)); is_committed=true; } MPI_Datatype get_mpi_datatype() const { return d; } ~mpi_datatype_holder() { int finalized=0; BOOST_MPI_CHECK_RESULT(MPI_Finalized,(&finalized)); if (!finalized && is_committed) BOOST_MPI_CHECK_RESULT(MPI_Type_free,(&d)); } private: MPI_Datatype d; bool is_committed; }; } // end namespace detail /** @brief A proxy object that transfers the content of an object * without its structure. * * The @c content class indicates that Boost.MPI should transmit or * receive the content of an object, but without any information * about the structure of the object. It is only meaningful to * transmit the content of an object after the receiver has already * received the skeleton for the same object. * * Most users will not use @c content objects directly. Rather, they * will invoke @c send, @c recv, or @c broadcast operations using @c * get_content(). */ class BOOST_MPI_DECL content { public: /** * Constructs an empty @c content object. This object will not be * useful for any Boost.MPI operations until it is reassigned. */ content() {} /** * This routine initializes the @c content object with an MPI data * type that refers to the content of an object without its structure. * * @param d the MPI data type referring to the content of the object. * * @param committed @c true indicates that @c MPI_Type_commit has * already been excuted for the data type @p d. */ content(MPI_Datatype d, bool committed=true) : holder(new detail::mpi_datatype_holder(d,committed)) {} /** * Replace the MPI data type referencing the content of an object. * * @param d the new MPI data type referring to the content of the * object. * * @returns *this */ const content& operator=(MPI_Datatype d) { holder.reset(new detail::mpi_datatype_holder(d)); return *this; } /** * Retrieve the MPI data type that refers to the content of the * object. * * @returns the MPI data type, which should only be transmitted or * received using @c MPI_BOTTOM as the address. */ MPI_Datatype get_mpi_datatype() const { return holder->get_mpi_datatype(); } /** * Commit the MPI data type referring to the content of the * object. */ void commit() { holder->commit(); } private: boost::shared_ptr<detail::mpi_datatype_holder> holder; }; /** @brief Returns the content of an object, suitable for transmission * via Boost.MPI. * * The function creates an absolute MPI datatype for the object, * where all offsets are counted from the address 0 (a.k.a. @c * MPI_BOTTOM) instead of the address @c &x of the object. This * allows the creation of MPI data types for complex data structures * containing pointers, such as linked lists or trees. * * The disadvantage, compared to relative MPI data types is that for * each object a new MPI data type has to be created. * * The contents of an object can only be transmitted when the * receiver already has an object with the same structure or shape as * the sender. To accomplish this, first transmit the skeleton of the * object using, e.g., @c skeleton() or @c skeleton_proxy. * * The type @c T has to allow creation of an absolute MPI data type * (content). * * @param x the object for which the content will be transmitted. * * @returns the content of the object @p x, which can be used for * transmission via @c send, @c recv, or @c broadcast. */ template <class T> const content get_content(const T& x); /** @brief An archiver that reconstructs a data structure based on the * binary skeleton stored in a buffer. * * The @c packed_skeleton_iarchive class is an Archiver (as in the * Boost.Serialization library) that can construct the the shape of a * data structure based on a binary skeleton stored in a buffer. The * @c packed_skeleton_iarchive is typically used by the receiver of a * skeleton, to prepare a data structure that will eventually receive * content separately. * * Users will not generally need to use @c packed_skeleton_iarchive * directly. Instead, use @c skeleton or @c get_skeleton. */ class BOOST_MPI_DECL packed_skeleton_iarchive : public detail::ignore_iprimitive, public detail::forward_skeleton_iarchive<packed_skeleton_iarchive,packed_iarchive> { public: /** * Construct a @c packed_skeleton_iarchive for the given * communicator. * * @param comm The communicator over which this archive will be * transmitted. * * @param flags Control the serialization of the skeleton. Refer to * the Boost.Serialization documentation before changing the * default flags. */ packed_skeleton_iarchive(MPI_Comm const & comm, unsigned int flags = boost::archive::no_header) : detail::forward_skeleton_iarchive<packed_skeleton_iarchive,packed_iarchive>(skeleton_archive_) , skeleton_archive_(comm,flags) {} /** * Construct a @c packed_skeleton_iarchive that unpacks a skeleton * from the given @p archive. * * @param archive the archive from which the skeleton will be * unpacked. * */ explicit packed_skeleton_iarchive(packed_iarchive & archive) : detail::forward_skeleton_iarchive<packed_skeleton_iarchive,packed_iarchive>(archive) , skeleton_archive_(MPI_COMM_WORLD, boost::archive::no_header) {} /** * Retrieve the archive corresponding to this skeleton. */ const packed_iarchive& get_skeleton() const { return this->implementation_archive; } /** * Retrieve the archive corresponding to this skeleton. */ packed_iarchive& get_skeleton() { return this->implementation_archive; } private: /// Store the actual archive that holds the structure, unless the /// user overrides this with their own archive. packed_iarchive skeleton_archive_; }; /** @brief An archiver that records the binary skeleton of a data * structure into a buffer. * * The @c packed_skeleton_oarchive class is an Archiver (as in the * Boost.Serialization library) that can record the shape of a data * structure (called the "skeleton") into a binary representation * stored in a buffer. The @c packed_skeleton_oarchive is typically * used by the send of a skeleton, to pack the skeleton of a data * structure for transmission separately from the content. * * Users will not generally need to use @c packed_skeleton_oarchive * directly. Instead, use @c skeleton or @c get_skeleton. */ class BOOST_MPI_DECL packed_skeleton_oarchive : public detail::ignore_oprimitive, public detail::forward_skeleton_oarchive<packed_skeleton_oarchive,packed_oarchive> { public: /** * Construct a @c packed_skeleton_oarchive for the given * communicator. * * @param comm The communicator over which this archive will be * transmitted. * * @param flags Control the serialization of the skeleton. Refer to * the Boost.Serialization documentation before changing the * default flags. */ packed_skeleton_oarchive(MPI_Comm const & comm, unsigned int flags = boost::archive::no_header) : detail::forward_skeleton_oarchive<packed_skeleton_oarchive,packed_oarchive>(skeleton_archive_) , skeleton_archive_(comm,flags) {} /** * Construct a @c packed_skeleton_oarchive that packs a skeleton * into the given @p archive. * * @param archive the archive to which the skeleton will be packed. * */ explicit packed_skeleton_oarchive(packed_oarchive & archive) : detail::forward_skeleton_oarchive<packed_skeleton_oarchive,packed_oarchive>(archive) , skeleton_archive_(MPI_COMM_WORLD, boost::archive::no_header) {} /** * Retrieve the archive corresponding to this skeleton. */ const packed_oarchive& get_skeleton() const { return this->implementation_archive; } private: /// Store the actual archive that holds the structure. packed_oarchive skeleton_archive_; }; namespace detail { typedef boost::mpi::detail::forward_skeleton_oarchive<boost::mpi::packed_skeleton_oarchive,boost::mpi::packed_oarchive> type1; typedef boost::mpi::detail::forward_skeleton_iarchive<boost::mpi::packed_skeleton_iarchive,boost::mpi::packed_iarchive> type2; } } } // end namespace boost::mpi #include <boost/mpi/detail/content_oarchive.hpp> // For any headers that have provided declarations based on forward // declarations of the contents of this header, include definitions // for those declarations. This means that the inclusion of // skeleton_and_content.hpp enables the use of skeleton/content // transmission throughout the library. #ifdef BOOST_MPI_BROADCAST_HPP # include <boost/mpi/detail/broadcast_sc.hpp> #endif #ifdef BOOST_MPI_COMMUNICATOR_HPP # include <boost/mpi/detail/communicator_sc.hpp> #endif // required by export BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::packed_skeleton_oarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::packed_skeleton_iarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::type1) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::type2) BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_skeleton_oarchive) BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_skeleton_iarchive) #endif // BOOST_MPI_SKELETON_AND_CONTENT_HPP