1 // (C) Copyright 2005 Matthias Troyer
2 // (C) Copyright 2006 Douglas Gregor <doug.gregor -at gmail.com>
3 
4 // Use, modification and distribution is subject to the Boost Software
5 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 
8 //  Authors: Matthias Troyer
9 //           Douglas Gregor
10 
11 /** @file skeleton_and_content.hpp
12  *
13  *  This header provides facilities that allow the structure of data
14  *  types (called the "skeleton") to be transmitted and received
15  *  separately from the content stored in those data types. These
16  *  facilities are useful when the data in a stable data structure
17  *  (e.g., a mesh or a graph) will need to be transmitted
18  *  repeatedly. In this case, transmitting the skeleton only once
19  *  saves both communication effort (it need not be sent again) and
20  *  local computation (serialization need only be performed once for
21  *  the content).
22  */
23 #ifndef BOOST_MPI_SKELETON_AND_CONTENT_HPP
24 #define BOOST_MPI_SKELETON_AND_CONTENT_HPP
25 
26 #include <boost/mpi/config.hpp>
27 #include <boost/archive/detail/auto_link_archive.hpp>
28 #include <boost/mpi/packed_iarchive.hpp>
29 #include <boost/mpi/packed_oarchive.hpp>
30 #include <boost/mpi/detail/forward_skeleton_iarchive.hpp>
31 #include <boost/mpi/detail/forward_skeleton_oarchive.hpp>
32 #include <boost/mpi/detail/ignore_iprimitive.hpp>
33 #include <boost/mpi/detail/ignore_oprimitive.hpp>
34 #include <boost/shared_ptr.hpp>
35 #include <boost/archive/detail/register_archive.hpp>
36 
37 namespace boost { namespace mpi {
38 
39 /**
40  *  @brief A proxy that requests that the skeleton of an object be
41  *  transmitted.
42  *
43  *  The @c skeleton_proxy is a lightweight proxy object used to
44  *  indicate that the skeleton of an object, not the object itself,
45  *  should be transmitted. It can be used with the @c send and @c recv
46  *  operations of communicators or the @c broadcast collective. When a
47  *  @c skeleton_proxy is sent, Boost.MPI generates a description
48  *  containing the structure of the stored object. When that skeleton
49  *  is received, the receiving object is reshaped to match the
50  *  structure. Once the skeleton of an object as been transmitted, its
51  *  @c content can be transmitted separately (often several times)
52  *  without changing the structure of the object.
53  */
54 template <class T>
55 struct BOOST_MPI_DECL skeleton_proxy
56 {
57   /**
58    *  Constructs a @c skeleton_proxy that references object @p x.
59    *
60    *  @param x the object whose structure will be transmitted or
61    *  altered.
62    */
skeleton_proxyboost::mpi::skeleton_proxy63   skeleton_proxy(T& x)
64    : object(x)
65   {}
66 
67   T& object;
68 };
69 
70 /**
71  *  @brief Create a skeleton proxy object.
72  *
73  *  This routine creates an instance of the skeleton_proxy class. It
74  *  will typically be used when calling @c send, @c recv, or @c
75  *  broadcast, to indicate that only the skeleton (structure) of an
76  *  object should be transmitted and not its contents.
77  *
78  *  @param x the object whose structure will be transmitted.
79  *
80  *  @returns a skeleton_proxy object referencing @p x
81  */
82 template <class T>
skeleton(T & x)83 inline const skeleton_proxy<T> skeleton(T& x)
84 {
85   return skeleton_proxy<T>(x);
86 }
87 
88 namespace detail {
89   /// @brief a class holding an MPI datatype
90   /// INTERNAL ONLY
91   /// the type is freed upon destruction
92   class BOOST_MPI_DECL mpi_datatype_holder : public boost::noncopyable
93   {
94   public:
mpi_datatype_holder()95     mpi_datatype_holder()
96      : is_committed(false)
97     {}
98 
mpi_datatype_holder(MPI_Datatype t,bool committed=true)99     mpi_datatype_holder(MPI_Datatype t, bool committed = true)
100      : d(t)
101      , is_committed(committed)
102     {}
103 
commit()104     void commit()
105     {
106       BOOST_MPI_CHECK_RESULT(MPI_Type_commit,(&d));
107       is_committed=true;
108     }
109 
get_mpi_datatype() const110     MPI_Datatype get_mpi_datatype() const
111     {
112       return d;
113     }
114 
~mpi_datatype_holder()115     ~mpi_datatype_holder()
116     {
117       int finalized=0;
118       BOOST_MPI_CHECK_RESULT(MPI_Finalized,(&finalized));
119       if (!finalized && is_committed)
120         BOOST_MPI_CHECK_RESULT(MPI_Type_free,(&d));
121     }
122 
123   private:
124     MPI_Datatype d;
125     bool is_committed;
126   };
127 } // end namespace detail
128 
129 /** @brief A proxy object that transfers the content of an object
130  *  without its structure.
131  *
132  *  The @c content class indicates that Boost.MPI should transmit or
133  *  receive the content of an object, but without any information
134  *  about the structure of the object. It is only meaningful to
135  *  transmit the content of an object after the receiver has already
136  *  received the skeleton for the same object.
137  *
138  *  Most users will not use @c content objects directly. Rather, they
139  *  will invoke @c send, @c recv, or @c broadcast operations using @c
140  *  get_content().
141  */
142 class BOOST_MPI_DECL content
143 {
144 public:
145   /**
146    *  Constructs an empty @c content object. This object will not be
147    *  useful for any Boost.MPI operations until it is reassigned.
148    */
content()149   content() {}
150 
151   /**
152    *  This routine initializes the @c content object with an MPI data
153    *  type that refers to the content of an object without its structure.
154    *
155    *  @param d the MPI data type referring to the content of the object.
156    *
157    *  @param committed @c true indicates that @c MPI_Type_commit has
158    *  already been excuted for the data type @p d.
159    */
content(MPI_Datatype d,bool committed=true)160   content(MPI_Datatype d, bool committed=true)
161    : holder(new detail::mpi_datatype_holder(d,committed))
162   {}
163 
164   /**
165    *  Replace the MPI data type referencing the content of an object.
166    *
167    *  @param d the new MPI data type referring to the content of the
168    *  object.
169    *
170    *  @returns *this
171    */
operator =(MPI_Datatype d)172   const content& operator=(MPI_Datatype d)
173   {
174     holder.reset(new detail::mpi_datatype_holder(d));
175     return *this;
176   }
177 
178   /**
179    * Retrieve the MPI data type that refers to the content of the
180    * object.
181    *
182    * @returns the MPI data type, which should only be transmitted or
183    * received using @c MPI_BOTTOM as the address.
184    */
get_mpi_datatype() const185   MPI_Datatype get_mpi_datatype() const
186   {
187     return holder->get_mpi_datatype();
188   }
189 
190   /**
191    *  Commit the MPI data type referring to the content of the
192    *  object.
193    */
commit()194   void commit()
195   {
196     holder->commit();
197   }
198 
199 private:
200   boost::shared_ptr<detail::mpi_datatype_holder> holder;
201 };
202 
203 /** @brief Returns the content of an object, suitable for transmission
204  *   via Boost.MPI.
205  *
206  *  The function creates an absolute MPI datatype for the object,
207  *  where all offsets are counted from the address 0 (a.k.a. @c
208  *  MPI_BOTTOM) instead of the address @c &x of the object. This
209  *  allows the creation of MPI data types for complex data structures
210  *  containing pointers, such as linked lists or trees.
211  *
212  *  The disadvantage, compared to relative MPI data types is that for
213  *  each object a new MPI data type has to be created.
214  *
215  *  The contents of an object can only be transmitted when the
216  *  receiver already has an object with the same structure or shape as
217  *  the sender. To accomplish this, first transmit the skeleton of the
218  *  object using, e.g., @c skeleton() or @c skeleton_proxy.
219  *
220  *  The type @c T has to allow creation of an absolute MPI data type
221  *  (content).
222  *
223  *  @param x the object for which the content will be transmitted.
224  *
225  *  @returns the content of the object @p x, which can be used for
226  *  transmission via @c send, @c recv, or @c broadcast.
227  */
228 template <class T> const content get_content(const T& x);
229 
230 /** @brief An archiver that reconstructs a data structure based on the
231  *  binary skeleton stored in a buffer.
232  *
233  *  The @c packed_skeleton_iarchive class is an Archiver (as in the
234  *  Boost.Serialization library) that can construct the the shape of a
235  *  data structure based on a binary skeleton stored in a buffer. The
236  *  @c packed_skeleton_iarchive is typically used by the receiver of a
237  *  skeleton, to prepare a data structure that will eventually receive
238  *  content separately.
239  *
240  *  Users will not generally need to use @c packed_skeleton_iarchive
241  *  directly. Instead, use @c skeleton or @c get_skeleton.
242  */
243 class BOOST_MPI_DECL packed_skeleton_iarchive
244   : public detail::ignore_iprimitive,
245     public detail::forward_skeleton_iarchive<packed_skeleton_iarchive,packed_iarchive>
246 {
247 public:
248   /**
249    *  Construct a @c packed_skeleton_iarchive for the given
250    *  communicator.
251    *
252    *  @param comm The communicator over which this archive will be
253    *  transmitted.
254    *
255    *  @param flags Control the serialization of the skeleton. Refer to
256    *  the Boost.Serialization documentation before changing the
257    *  default flags.
258    */
packed_skeleton_iarchive(MPI_Comm const & comm,unsigned int flags=boost::archive::no_header)259   packed_skeleton_iarchive(MPI_Comm const & comm,
260                            unsigned int flags =  boost::archive::no_header)
261          : detail::forward_skeleton_iarchive<packed_skeleton_iarchive,packed_iarchive>(skeleton_archive_)
262          , skeleton_archive_(comm,flags)
263         {}
264 
265   /**
266    *  Construct a @c packed_skeleton_iarchive that unpacks a skeleton
267    *  from the given @p archive.
268    *
269    *  @param archive the archive from which the skeleton will be
270    *  unpacked.
271    *
272    */
packed_skeleton_iarchive(packed_iarchive & archive)273   explicit packed_skeleton_iarchive(packed_iarchive & archive)
274          : detail::forward_skeleton_iarchive<packed_skeleton_iarchive,packed_iarchive>(archive)
275          , skeleton_archive_(MPI_COMM_WORLD, boost::archive::no_header)
276         {}
277 
278   /**
279    *  Retrieve the archive corresponding to this skeleton.
280    */
get_skeleton() const281   const packed_iarchive& get_skeleton() const
282   {
283     return this->implementation_archive;
284   }
285 
286   /**
287    *  Retrieve the archive corresponding to this skeleton.
288    */
get_skeleton()289   packed_iarchive& get_skeleton()
290   {
291     return this->implementation_archive;
292   }
293 
294 private:
295   /// Store the actual archive that holds the structure, unless the
296   /// user overrides this with their own archive.
297   packed_iarchive skeleton_archive_;
298 };
299 
300 /** @brief An archiver that records the binary skeleton of a data
301  * structure into a buffer.
302  *
303  *  The @c packed_skeleton_oarchive class is an Archiver (as in the
304  *  Boost.Serialization library) that can record the shape of a data
305  *  structure (called the "skeleton") into a binary representation
306  *  stored in a buffer. The @c packed_skeleton_oarchive is typically
307  *  used by the send of a skeleton, to pack the skeleton of a data
308  *  structure for transmission separately from the content.
309  *
310  *  Users will not generally need to use @c packed_skeleton_oarchive
311  *  directly. Instead, use @c skeleton or @c get_skeleton.
312  */
313 class BOOST_MPI_DECL packed_skeleton_oarchive
314   : public detail::ignore_oprimitive,
315     public detail::forward_skeleton_oarchive<packed_skeleton_oarchive,packed_oarchive>
316 {
317 public:
318   /**
319    *  Construct a @c packed_skeleton_oarchive for the given
320    *  communicator.
321    *
322    *  @param comm The communicator over which this archive will be
323    *  transmitted.
324    *
325    *  @param flags Control the serialization of the skeleton. Refer to
326    *  the Boost.Serialization documentation before changing the
327    *  default flags.
328    */
packed_skeleton_oarchive(MPI_Comm const & comm,unsigned int flags=boost::archive::no_header)329   packed_skeleton_oarchive(MPI_Comm const & comm,
330                            unsigned int flags =  boost::archive::no_header)
331          : detail::forward_skeleton_oarchive<packed_skeleton_oarchive,packed_oarchive>(skeleton_archive_)
332          , skeleton_archive_(comm,flags)
333         {}
334 
335   /**
336    *  Construct a @c packed_skeleton_oarchive that packs a skeleton
337    *  into the given @p archive.
338    *
339    *  @param archive the archive to which the skeleton will be packed.
340    *
341    */
packed_skeleton_oarchive(packed_oarchive & archive)342   explicit packed_skeleton_oarchive(packed_oarchive & archive)
343          : detail::forward_skeleton_oarchive<packed_skeleton_oarchive,packed_oarchive>(archive)
344          , skeleton_archive_(MPI_COMM_WORLD, boost::archive::no_header)
345         {}
346 
347   /**
348    *  Retrieve the archive corresponding to this skeleton.
349    */
get_skeleton() const350   const packed_oarchive& get_skeleton() const
351   {
352     return this->implementation_archive;
353   }
354 
355 private:
356   /// Store the actual archive that holds the structure.
357   packed_oarchive skeleton_archive_;
358 };
359 
360 namespace detail {
361   typedef boost::mpi::detail::forward_skeleton_oarchive<boost::mpi::packed_skeleton_oarchive,boost::mpi::packed_oarchive> type1;
362   typedef boost::mpi::detail::forward_skeleton_iarchive<boost::mpi::packed_skeleton_iarchive,boost::mpi::packed_iarchive> type2;
363 }
364 
365 
366 } } // end namespace boost::mpi
367 
368 #include <boost/mpi/detail/content_oarchive.hpp>
369 
370 // For any headers that have provided declarations based on forward
371 // declarations of the contents of this header, include definitions
372 // for those declarations. This means that the inclusion of
373 // skeleton_and_content.hpp enables the use of skeleton/content
374 // transmission throughout the library.
375 #ifdef BOOST_MPI_BROADCAST_HPP
376 #  include <boost/mpi/detail/broadcast_sc.hpp>
377 #endif
378 
379 #ifdef BOOST_MPI_COMMUNICATOR_HPP
380 #  include <boost/mpi/detail/communicator_sc.hpp>
381 #endif
382 
383 // required by export
384 BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::packed_skeleton_oarchive)
385 BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::packed_skeleton_iarchive)
386 BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::type1)
387 BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::type2)
388 
389 BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_skeleton_oarchive)
390 BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_skeleton_iarchive)
391 
392 #endif // BOOST_MPI_SKELETON_AND_CONTENT_HPP
393