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