TIMPI
Loading...
Searching...
No Matches
packing.h
Go to the documentation of this file.
1// The TIMPI Message-Passing Parallelism Library.
2// Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
19#ifndef TIMPI_PACKING_H
20#define TIMPI_PACKING_H
21
22// TIMPI Includes
23#include "timpi/packing_decl.h"
24
25#include "timpi/timpi_assert.h"
26
27// C++ includes
28#include <climits> // CHAR_BIT
29#include <cstring> // memcpy
30#include <iterator>
31#include <type_traits> // is_same
32
33// C++17 gives us "if constexpr", which nvc++ really seems to need in
34// order to determine that warnings shouldn't be emitted inside a
35// block instantiated as "if (32 < 32)", but let's see if we can
36// stay backwards compatible a while longer
37#if __cplusplus >= 201703
38# define timpi_if_constexpr if constexpr
39#else
40# define timpi_if_constexpr if
41#endif
42
43
44// FIXME: This *should* be in TIMPI namespace but we have libMesh
45// users which already partially specialized it
46namespace libMesh
47{
48
49namespace Parallel
50{
51
59template <typename T, typename Enable>
60class Packing {
61public:
62 // Should be an MPI sendable type in specializations, e.g.
63 // typedef char buffer_type;
64 // typedef unsigned int buffer_type;
65
66 // The following methods should be defined in every specialization,
67 // but there's no good generic definition we can fall back on.
68 // Leaving undefined generic methods declared here would turn
69 // missing-header bugs into link-time failures rather than
70 // compile-time failures - accordingly harder to diagnose.
71#if 0
72 // Should copy an encoding of the provided object into the provided
73 // output iterator (which is of type buffer_type)
74 template <typename OutputIter, typename Context>
75 static void pack(const T & object,
76 OutputIter data_out,
77 const Context * context);
78
79 // Should return the number of array entries (of type buffer_type)
80 // required to encode the provided object
81 template <typename Context>
82 static unsigned int packable_size(const T & object,
83 const Context * context);
84
85 // Should return the number of array entries which were used to
86 // encode the provided serialization of an object which begins at
87 // \p iter
88 template <typename BufferIter>
89 static unsigned int packed_size(BufferIter iter);
90
91 // Decode a potentially-variable-size object from a subsequence of a
92 // data array, returning a heap-allocated pointer to the result.
93 template <typename BufferIter, typename Context>
94 static T unpack(BufferIter in, Context * ctx);
95#endif
96};
97
98
99// Utility functions for encoding and decoding lengths into buffers
100// with data types that may be too small to hold an unsigned int. For
101// MPI compatibility we assume that lengths do fit into an int.
102template <typename buffer_type>
103inline
104constexpr int
106{
107 return
108 (sizeof(unsigned int) + (sizeof(buffer_type)-1)) /
109 sizeof(buffer_type);
110}
111
112
113template <typename buffer_type, typename Iter>
114inline
115void
116put_packed_len (unsigned int len, Iter data_out)
117{
118 // I hoped decltype(*data_out) would always be buffer_type, but no dice
119
120 // If we're using 2-byte or 1-byte buffer type then we have to split
121 // into multiple entries
122 constexpr int n_bits = (sizeof(buffer_type) * CHAR_BIT);
123
124 // We may have a small signed buffer type into which we stuffed
125 // an unsigned value
126 if (n_bits < sizeof(unsigned int) * CHAR_BIT)
127 {
128 constexpr int size_entries = get_packed_len_entries<buffer_type>();
129
130 // Some compilers warn about shifting by too many bits even when
131 // that's unreachable code. After we require C++17 we should
132 // see if "if constexpr" is a better ifx.
133 constexpr int compiler_workaround =
134 std::max(n_bits-1, int(sizeof(unsigned int) * CHAR_BIT));
135
136 const std::size_t max_entry = std::size_t(1) <<
137 compiler_workaround;
138
139 for (unsigned int i=0; i != size_entries; ++i)
140 {
141 *data_out++ = (len % max_entry);
142 len /= max_entry;
143 }
144
145 return;
146 }
147
148 // With 32 bits or more this is trivial
149 timpi_assert_equal_to(get_packed_len_entries<buffer_type>(), 1);
150 *data_out++ = len;
151}
152
153
154template <typename buffer_type>
155inline
156unsigned int
157get_packed_len (typename std::vector<buffer_type>::const_iterator in)
158{
159 // If we're using 2-byte or 1-byte buffer type then we have to split
160 // into multiple entries
161 constexpr int n_bits = (sizeof(buffer_type) * CHAR_BIT);
162
163 // We may have a small signed buffer type into which we stuffed
164 // an unsigned value. Try to use constexpr here for efficiency and
165 // to avoid shift size warnings.
166 timpi_if_constexpr (n_bits < sizeof(unsigned int) * CHAR_BIT)
167 {
168 const int n_size_entries = get_packed_len_entries<buffer_type>();
169 unsigned int packed_len = 0;
170
171 for (signed int i = n_size_entries-1; i >= 0; --i)
172 {
173 packed_len <<= n_bits;
174
175 const auto next_entry = in[i];
176
177 if (next_entry < 0)
178 packed_len += 1 << n_bits;
179
180 packed_len += next_entry;
181 }
182 return packed_len;
183 }
184
185 // With 32 bits or more this is trivial
186
187 timpi_assert_equal_to(get_packed_len_entries<buffer_type>(), 1);
188 timpi_assert_greater_equal(*in, 0);
189
190 return *in;
191}
192
193
194// Metafunction to get a value_type from map and unordered_map with
195// non-const keys, so we can create a key/value pair more easily
196template <typename ValueType>
198 typedef ValueType type;
199};
200
201template <typename K, typename V>
202struct DefaultValueType<std::pair<const K, V>> {
203 typedef std::pair<K, V> type;
204};
205
206
207// Superclass with utility methods for use with Packing partial
208// specializations that mix fixed-size with Packing-required inner
209// classes.
210template <typename BufferType>
212{
213 typedef BufferType buffer_type;
214
215 template <typename T3>
216 struct IsFixed
217 {
218 static const bool value =
220 <typename DefaultValueType<T3>::type>::is_fixed_type;
221 };
222
223 template <typename T3>
225 {
226 static const unsigned int value = (sizeof(T3) + sizeof(buffer_type) - 1) / sizeof(buffer_type);
227 };
228
229 template <typename T3,
230 typename Context,
231 typename std::enable_if<IsFixed<T3>::value, int>::type = 0>
232 static unsigned int packable_size_comp(const T3 &, const Context *)
233 {
235 }
236
237 // By not just doing memcpy here we can drop any padding
238 template <typename T1, typename T2,
239 typename Context,
240 typename std::enable_if<IsFixed<std::pair<T1, T2>>::value, int>::type = 0>
241 static unsigned int packable_size_comp(const std::pair<T1, T2> & comp, const Context * ctx)
242 {
243 return packable_size_comp(comp.first, ctx) +
244 packable_size_comp(comp.second, ctx);
245 }
246
247 template <typename T3,
248 typename Context,
249 typename std::enable_if<!IsFixed<T3>::value, int>::type = 0>
250 static unsigned int packable_size_comp(const T3 & comp, const Context * ctx)
251 {
252 return Packing<T3>::packable_size(comp, ctx);
253 }
254
255// g++ 11.2.0 gives "not protecting ... less than 8 bytes long" here,
256// and that's more paranoid than we wanted --enable-paranoid-warnings
257// to be...
258
259#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
260#pragma GCC diagnostic push
261#pragma GCC diagnostic ignored "-Wstack-protector"
262#endif
263
264 template <typename T3,
265 typename OutputIter,
266 typename Context,
267 typename std::enable_if<IsFixed<T3>::value, int>::type = 0>
268 static void pack_comp(const T3 & comp, OutputIter data_out, const Context *)
269 {
270 buffer_type T3_as_buffer_types[BufferTypesPer<T3>::value];
271 std::memcpy(T3_as_buffer_types, &comp, sizeof(T3));
272 for (unsigned int i = 0; i != BufferTypesPer<T3>::value; ++i)
273 *data_out++ = T3_as_buffer_types[i];
274 }
275
276#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
277#pragma GCC diagnostic pop
278#endif
279
280 // By not just doing memcpy here we can drop any padding
281 template <typename T1, typename T2,
282 typename OutputIter,
283 typename Context,
284 typename std::enable_if<IsFixed<std::pair<T1, T2>>::value, int>::type = 0>
285 static void pack_comp(const std::pair<T1, T2> & comp, OutputIter data_out, const Context * ctx)
286 {
287 pack_comp(comp.first, data_out, ctx);
288 pack_comp(comp.second, data_out, ctx);
289 }
290
291 template <typename T3,
292 typename OutputIter,
293 typename Context,
294 typename std::enable_if<!IsFixed<T3>::value, int>::type = 0>
295 static void pack_comp(const T3 & comp, OutputIter data_out, const Context * ctx)
296 {
297 Packing<T3>::pack(comp, data_out, ctx);
298 }
299
300#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
301#pragma GCC diagnostic push
302#pragma GCC diagnostic ignored "-Wstack-protector"
303#endif
304
305 template <typename T3,
306 typename BufferIter,
307 typename Context,
308 typename std::enable_if<IsFixed<T3>::value, int>::type = 0>
309 static void unpack_comp(T3 & comp, BufferIter in, Context *)
310 {
311 // memcpy is only safe to use with classes that are trivial to
312 // copy construct
313 //
314 // In this function overload, the enable_if<IsFixed> has already
315 // determined that we're safe in that respect.
316 //
317 // But the C++ standards don't mandate that important types like
318 // std::tuple ever satisfy is_trivially_copyable, and gcc goes so
319 // far as to emit warnings based on is_trivial instead, so we need
320 // to work around that here.
321 // https://gcc.gnu.org/legacy-ml/gcc-patches/2017-07/msg00299.html
322 char * comp_bytes = reinterpret_cast<char *>(&comp);
323 std::memcpy(comp_bytes, &(*in), sizeof(T3));
324 }
325
326#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
327#pragma GCC diagnostic pop
328#endif
329
330 template <typename T1, typename T2,
331 typename BufferIter,
332 typename Context,
333 typename std::enable_if<IsFixed<std::pair<T1, T2>>::value, int>::type = 0>
334 static void unpack_comp(std::pair<T1, T2> & comp, BufferIter in, Context * ctx)
335 {
336 unpack_comp(comp.first, in, ctx);
337
338 in += packable_size_comp(comp.first, ctx);
339
340 unpack_comp(comp.second, in, ctx);
341 }
342
343
344 template <typename T3,
345 typename BufferIter,
346 typename Context,
347 typename std::enable_if<!IsFixed<T3>::value, int>::type = 0>
348 static void unpack_comp(T3 & comp, BufferIter in, Context * ctx)
349 {
350 comp = Packing<T3>::unpack(in, ctx);
351 }
352};
353
354
355
356
357// Utility metafunction for use in Packing<std::pair>
358template <typename T1, bool T1_has_buffer_type, typename T2, bool T2_has_buffer_type>
360
361template <typename T1, typename T2>
362struct PairBufferTypeHelper<T1, true, T2, true>
363{
364 static_assert(std::is_same<typename Packing<T1>::buffer_type, typename Packing<T2>::buffer_type>::value,
365 "For ease of use we cannot pack two types that use two different buffer types");
366
368};
369
370template <typename T1, typename T2>
371struct PairBufferTypeHelper<T1, true, T2, false>
372{
374};
375
376template <typename T1, typename T2>
377struct PairBufferTypeHelper<T1, false, T2, true>
378{
380};
381
382template <typename T1, typename T2>
383struct PairBufferTypeHelper<T1, false, T2, false>
384{
385 typedef unsigned int buffer_type;
386};
387
388
389// specialization for std::pair
390template <typename T1, typename T2>
391class Packing<std::pair<T1, T2>,
392 typename std::enable_if<PairHasPacking<T1,T2>::value>::type>
393{
394public:
395 typedef typename std::remove_const<T1>::type cT1;
396
397 typedef typename PairBufferTypeHelper
399 T2, Has_buffer_type<Packing<T2>>::value>::buffer_type buffer_type;
400
402
403 template <typename OutputIter, typename Context>
404 static void pack(const std::pair<T1, T2> & pr, OutputIter data_out, const Context * context);
405
406 template <typename Context>
407 static unsigned int packable_size(const std::pair<T1, T2> & pr, const Context * context);
408
409 template <typename BufferIter>
410 static unsigned int packed_size(BufferIter iter);
411
412 template <typename BufferIter, typename Context>
413 static std::pair<T1, T2> unpack(BufferIter in, Context * ctx);
414};
415
416template <typename T1, typename T2>
417template <typename Context>
418unsigned int
420 typename std::enable_if<PairHasPacking<T1,T2>::value>::type>::
421 packable_size(const std::pair<T1, T2> & pr, const Context * ctx)
422{
423 return get_packed_len_entries<buffer_type>() +
424 Mixed::packable_size_comp(pr.first, ctx) +
425 Mixed::packable_size_comp(pr.second, ctx);
426}
427
428template <typename T1, typename T2>
429template <typename BufferIter>
430unsigned int
432 typename std::enable_if<PairHasPacking<T1,T2>::value>::type>::
433 packed_size(BufferIter iter)
434{
435 // We recorded the size in the first buffer entries
436 return get_packed_len<buffer_type>(iter);
437}
438
439template <typename T1, typename T2>
440template <typename OutputIter, typename Context>
441void
443 typename std::enable_if<PairHasPacking<T1,T2>::value>::type>::
444 pack(const std::pair<T1, T2> & pr, OutputIter data_out, const Context * ctx)
445{
446 unsigned int size = packable_size(pr, ctx);
447
448 // First write out info about the buffer size
449 put_packed_len<buffer_type>(size, data_out);
450
451 // Now pack the data
452 Mixed::pack_comp(pr.first, data_out, ctx);
453
454 // TIMPI uses a back_inserter for `pack_range` so we don't (and can't)
455 // actually increment the iterator with operator+=. operator++ is a no-op
456 //
457 // data_out += packable_size_comp(pr.first, ctx);
458
459 Mixed::pack_comp(pr.second, data_out, ctx);
460}
461
462template <typename T1, typename T2>
463template <typename BufferIter, typename Context>
464std::pair<T1, T2>
466 typename std::enable_if<PairHasPacking<T1,T2>::value>::type>::
467 unpack(BufferIter in, Context * ctx)
468{
469 std::pair<T1, T2> pr;
470
471 // We ignore total size here but we have to increment past it
472 constexpr int size_bytes = get_packed_len_entries<buffer_type>();
473 in += size_bytes;
474
475 // Unpack the data
476 Mixed::unpack_comp(pr.first, in, ctx);
477
478 // Make sure we increment the iterator
479 in += Mixed::packable_size_comp(pr.first, ctx);
480
481 Mixed::unpack_comp(pr.second, in, ctx);
482
483 return pr;
484}
485
486
487
488
489template <bool T1_has_buffer_type, bool MoreTypes_have_buffer_Type, typename T1, typename... MoreTypes>
491
492template <typename T1, bool MoreTypes_have_buffer_Type>
493struct TupleBufferTypeHelper<true, MoreTypes_have_buffer_Type, T1> {
495};
496
497template <typename T1, typename... MoreTypes>
498struct TupleBufferTypeHelper<true, true, T1, MoreTypes...> {
499 static_assert(std::is_same<typename Packing<T1>::buffer_type,
500 typename Packing<std::tuple<MoreTypes...>>::buffer_type>::value,
501 "For ease of use we cannot pack two types that use two different buffer types");
502
504};
505
506template <typename T1, typename... MoreTypes>
507struct TupleBufferTypeHelper<true, false, T1, MoreTypes...> {
509};
510
511template <typename T1, typename... MoreTypes>
512struct TupleBufferTypeHelper<false, true, T1, MoreTypes...> {
513 typedef typename Packing<std::tuple<MoreTypes...>>::buffer_type buffer_type;
514};
515
516template <typename... Types>
518
519
520template <typename T1>
522{
524};
525
526
527template <typename T1, typename... MoreTypes>
528struct TupleBufferType<T1, MoreTypes...>
529{
530 typedef typename
532 Has_buffer_type<Packing<std::tuple<MoreTypes...>>>::value,
533 T1, MoreTypes...>::buffer_type buffer_type;
534};
535
536
537
538// specializations for std::tuple
539template <typename Enable>
540class Packing<std::tuple<>, Enable> {};
541
542template <typename T, typename... Types>
543class Packing<std::tuple<T, Types...>,
544 typename std::enable_if<TupleHasPacking<T, Types...>::value>::type>
545{
546public:
547 typedef typename TupleBufferType<T, Types...>::buffer_type buffer_type;
548
550
551 template <typename OutputIter, typename Context>
552 static void pack(const std::tuple<T, Types...> & tup, OutputIter data_out, const Context * context);
553
554 template <typename Context>
555 static unsigned int packable_size(const std::tuple<T, Types...> & tup, const Context * context);
556
557 template <typename BufferIter>
558 static unsigned int packed_size(BufferIter iter);
559
560 template <typename BufferIter, typename Context>
561 static std::tuple<T, Types...> unpack(BufferIter in, Context * ctx);
562
563 template <typename Context,
564 std::size_t I>
565 static typename std::enable_if<I == sizeof...(Types)+1, unsigned int>::type
566 tail_packable_size(const std::tuple<T, Types...> &,
567 const Context *)
568 { return 0; }
569
570 template <typename Context,
571 std::size_t I>
572 static typename std::enable_if<I < sizeof...(Types)+1, unsigned int>::type
573 tail_packable_size(const std::tuple<T, Types...> &tup,
574 const Context * ctx)
575 {
576 return Mixed::packable_size_comp(std::get<I>(tup), ctx) +
577 tail_packable_size<Context, I+1>(tup, ctx);
578 }
579
580 template <typename Context,
581 typename OutputIter,
582 std::size_t I>
583 static typename std::enable_if<I == sizeof...(Types)+1, void>::type
584 tail_pack_comp(const std::tuple<T, Types...> &,
585 OutputIter,
586 const Context *) {}
587
588 template <typename Context,
589 typename OutputIter,
590 std::size_t I>
591 static typename std::enable_if<I < sizeof...(Types)+1, void>::type
592 tail_pack_comp(const std::tuple<T, Types...> &tup,
593 OutputIter data_out,
594 const Context * ctx)
595 {
596 Mixed::pack_comp(std::get<I>(tup), data_out, ctx);
597 tail_pack_comp<Context, OutputIter, I+1>(tup, data_out, ctx);
598 }
599
600 template <typename Context,
601 typename BufferIter,
602 std::size_t I>
603 static typename std::enable_if<I == sizeof...(Types)+1, void>::type
604 tail_unpack_comp(std::tuple<T, Types...> &,
605 BufferIter &,
606 Context *) {}
607
608 template <typename Context,
609 typename BufferIter,
610 std::size_t I>
611 static typename std::enable_if<I < sizeof...(Types)+1, void>::type
612 tail_unpack_comp(std::tuple<T, Types...> &tup,
613 BufferIter & in,
614 Context * ctx)
615 {
616 Mixed::unpack_comp(std::get<I>(tup), in, ctx);
617
618 // Make sure we increment the iterator. The last increment will
619 // be unnecessary, since this is a copy of the iterator in the
620 // higher level unpacking code, but the first N-1 are critical.
621 in += Mixed::packable_size_comp(std::get<I>(tup), ctx);
622
623 tail_unpack_comp<Context, BufferIter, I+1>(tup, in, ctx);
624 }
625};
626
627
628template <typename T, typename... Types>
629template <typename Context>
630unsigned int
631Packing<std::tuple<T, Types...>,
632 typename std::enable_if<TupleHasPacking<T, Types...>::value>::type>::
633 packable_size(const std::tuple<T, Types...> & tup, const Context * ctx)
634{
635 return get_packed_len_entries<buffer_type>() +
636 tail_packable_size<Context, 0>(tup, ctx);
637}
638
639template <typename T, typename... Types>
640template <typename BufferIter>
641unsigned int
642Packing<std::tuple<T, Types...>,
643 typename std::enable_if<TupleHasPacking<T, Types...>::value>::type>::
644 packed_size(BufferIter iter)
645{
646 // We recorded the size in the first buffer entries
647 return get_packed_len<buffer_type>(iter);
648}
649
650template <typename T, typename... Types>
651template <typename OutputIter, typename Context>
652void
653Packing<std::tuple<T, Types...>,
654 typename std::enable_if<TupleHasPacking<T, Types...>::value>::type>::
655 pack(const std::tuple<T, Types...> & tup, OutputIter data_out, const Context * ctx)
656{
657 unsigned int size = packable_size(tup, ctx);
658
659 // First write out info about the buffer size
660 put_packed_len<buffer_type>(size, data_out);
661
662 // Now pack the data
663 tail_pack_comp<Context, OutputIter, 0>(tup, data_out, ctx);
664}
665
666template <typename T, typename... Types>
667template <typename BufferIter, typename Context>
668std::tuple<T, Types...>
669Packing<std::tuple<T, Types...>,
670 typename std::enable_if<TupleHasPacking<T, Types...>::value>::type>::
671 unpack(BufferIter in, Context * ctx)
672{
673 std::tuple<T, Types...> tup;
674
675 // We ignore total size here but we have to increment past it
676 constexpr int size_bytes = get_packed_len_entries<buffer_type>();
677 in += size_bytes;
678
679 // Unpack the data
680 tail_unpack_comp<Context, BufferIter, 0>(tup, in, ctx);
681
682 return tup;
683}
684
685
686
687// specialization for std::array
688template <typename T, std::size_t N>
689class Packing<std::array<T, N>,
690 typename std::enable_if<Has_buffer_type<Packing<T>>::value>::type>
691{
692public:
694
696
697 template <typename OutputIter, typename Context>
698 static void pack(const std::array<T, N> & a, OutputIter data_out, const Context * context);
699
700 template <typename Context>
701 static unsigned int packable_size(const std::array<T, N> & a, const Context * context);
702
703 template <typename BufferIter>
704 static unsigned int packed_size(BufferIter iter);
705
706 template <typename BufferIter, typename Context>
707 static std::array<T, N> unpack(BufferIter in, Context * ctx);
708};
709
710template <typename T, std::size_t N>
711template <typename Context>
712unsigned int
714 typename std::enable_if<Has_buffer_type<Packing<T>>::value>::type>::
715 packable_size(const std::array<T, N> & a, const Context * ctx)
716{
717 unsigned int returnval = get_packed_len_entries<buffer_type>(); // size
718 for (const auto & entry : a)
719 returnval += Mixed::packable_size_comp(entry, ctx);
720 return returnval;
721}
722
723template <typename T, std::size_t N>
724template <typename BufferIter>
725unsigned int
727 typename std::enable_if<Has_buffer_type<Packing<T>>::value>::type>::
728 packed_size(BufferIter iter)
729{
730 // We recorded the size in the first buffer entries
731 return get_packed_len<buffer_type>(iter);
732}
733
734template <typename T, std::size_t N>
735template <typename OutputIter, typename Context>
736void
737Packing<std::array<T, N>,
738 typename std::enable_if<Has_buffer_type<Packing<T>>::value>::type>::
739 pack(const std::array<T, N> & a, OutputIter data_out, const Context * ctx)
740{
741 unsigned int size = packable_size(a, ctx);
742
743 // First write out info about the buffer size
744 put_packed_len<buffer_type>(size, data_out);
745
746 // Now pack the data
747 for (const auto & entry : a)
748 Mixed::pack_comp(entry, data_out, ctx);
749}
750
751template <typename T, std::size_t N>
752template <typename BufferIter, typename Context>
753std::array<T, N>
755 typename std::enable_if<Has_buffer_type<Packing<T>>::value>::type>::
756 unpack(BufferIter in, Context * ctx)
757{
758 std::array<T, N> a;
759
760 // We ignore total size here but we have to increment past it
761 constexpr int size_bytes = get_packed_len_entries<buffer_type>();
762 in += size_bytes;
763
764 // Unpack the data
765 for (auto & entry : a)
766 {
767 Mixed::unpack_comp(entry, in, ctx);
768
769 // Make sure we increment the iterator
770 in += Mixed::packable_size_comp(entry, ctx);
771 }
772
773 return a;
774}
775
776
777
778// Metafunction to choose buffer types: use a specified
779// Packing<class>::buffer_type for any class that has one; use
780// unsigned int otherwise.
781template <typename T, typename Enable=void>
783
784template <typename T>
785struct DefaultBufferType <T, typename std::enable_if<Has_buffer_type<Packing<T>>::value>::type>
786{
788};
789
790template <typename T>
791struct DefaultBufferType <T, typename std::enable_if<!Has_buffer_type<Packing<T>>::value>::type>
792{
793 typedef unsigned int type;
794};
795
796
797// helper class for any homogeneous-type variable-size containers
798// which define the usual iterator ranges, value_type, etc.
799template <typename Container>
801{
802public:
803 typedef typename
806
808
809 template <typename OutputIter, typename Context>
810 static void pack(const Container & a,
811 OutputIter data_out, const Context * context);
812
813 template <typename Context>
814 static unsigned int packable_size(const Container & a,
815 const Context * context);
816
817 template <typename BufferIter>
818 static unsigned int packed_size(BufferIter iter);
819
820 template <typename BufferIter, typename Context>
821 static Container unpack(BufferIter in, Context * ctx);
822};
823
824
825template <typename Container>
826template <typename Context>
827unsigned int
828PackingRange<Container>::packable_size(const Container & c, const Context * ctx)
829{
830 unsigned int returnval = get_packed_len_entries<buffer_type>(); // size
831 for (const auto & entry : c)
832 returnval += Mixed::packable_size_comp(entry, ctx);
833 return returnval;
834}
835
836template <typename Container>
837template <typename BufferIter>
838unsigned int
840{
841 // We recorded the size in the first buffer entries
842 return get_packed_len<buffer_type>(iter);
843}
844
845template <typename Container>
846template <typename OutputIter, typename Context>
847void
848PackingRange<Container>::pack(const Container & c, OutputIter data_out, const Context * ctx)
849{
850 unsigned int size = packable_size(c, ctx);
851
852 // First write out info about the buffer size
853 put_packed_len<buffer_type>(size, data_out);
854
855 // Now pack the data
856 for (const auto & entry : c)
857 Mixed::pack_comp(entry, data_out, ctx);
858}
859
860template <typename Container>
861template <typename BufferIter, typename Context>
862Container
863PackingRange<Container>::unpack(BufferIter in, Context * ctx)
864{
865 Container c;
866
867 unsigned int size = packed_size(in);
868
869 timpi_assert_greater(size, 0);
870
871 // Get the total size
872 constexpr int size_bytes = get_packed_len_entries<buffer_type>();
873 in += size_bytes;
874 size -= size_bytes;
875
876 // Unpack the data
877 std::size_t unpacked_size = 0;
878 while (unpacked_size < size)
879 {
881 Mixed::unpack_comp(entry, in, ctx);
882
883 c.insert(c.end(), entry);
884
885 // Make sure we increment the iterator
886 const std::size_t unpacked_size_comp =
887 Mixed::packable_size_comp(entry, ctx);
888 in += unpacked_size_comp;
889 unpacked_size += unpacked_size_comp;
890 }
891
892 // We should always finish at exactly the size we expected, not
893 // proceed past it
894 timpi_assert_equal_to(unpacked_size, size);
895
896 return c;
897}
898
899
900
901#define TIMPI_PACKING_RANGE_SUBCLASS(Container) \
902class Packing<Container, \
903 typename std::enable_if<Has_buffer_type<Packing<typename Container::value_type>>::value || \
904 TIMPI::StandardType<typename Container::value_type>::is_fixed_type>::type> : \
905 public PackingRange<Container> \
906{ \
907public: \
908 using typename PackingRange<Container>::buffer_type; \
909 \
910 using typename PackingRange<Container>::Mixed; \
911 \
912 using PackingRange<Container>::pack; \
913 using PackingRange<Container>::packable_size; \
914 using PackingRange<Container>::packed_size; \
915 using PackingRange<Container>::unpack; \
916}
917
918
919template <typename T, typename A>
920TIMPI_PACKING_RANGE_SUBCLASS(std::vector<T TIMPI_P_COMMA A>);
921
922template <typename T, typename A>
923TIMPI_PACKING_RANGE_SUBCLASS(std::list<T TIMPI_P_COMMA A>);
924
925template <typename K, typename T, typename C, typename A>
926TIMPI_PACKING_RANGE_SUBCLASS(std::map<K TIMPI_P_COMMA T TIMPI_P_COMMA C TIMPI_P_COMMA A>);
927
928template <typename K, typename T, typename C, typename A>
929TIMPI_PACKING_RANGE_SUBCLASS(std::multimap<K TIMPI_P_COMMA T TIMPI_P_COMMA C TIMPI_P_COMMA A>);
930
931template <typename K, typename C, typename A>
932TIMPI_PACKING_RANGE_SUBCLASS(std::multiset<K TIMPI_P_COMMA C TIMPI_P_COMMA A>);
933
934template <typename K, typename C, typename A>
935TIMPI_PACKING_RANGE_SUBCLASS(std::set<K TIMPI_P_COMMA C TIMPI_P_COMMA A>);
936
937template <typename K, typename T, typename H, typename KE, typename A>
938TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_map<K TIMPI_P_COMMA T TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
939
940template <typename K, typename T, typename H, typename KE, typename A>
941TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_multimap<K TIMPI_P_COMMA T TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
942
943template <typename K, typename H, typename KE, typename A>
944TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_multiset<K TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
945
946template <typename K, typename H, typename KE, typename A>
947TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_set<K TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
948
949
950template <typename T>
951class Packing<std::basic_string<T>,
952 typename std::enable_if<TIMPI::StandardType<T>::is_fixed_type>::type>
953{
954public:
955
956 typedef unsigned int buffer_type;
957
958 static_assert(sizeof(T) <= sizeof(buffer_type),
959 "We don't support strings with larger characters than unsigned int");
960
961 static constexpr int T_per_buffer_type = (sizeof(buffer_type) + sizeof(T) - 1)/sizeof(T);
962
963 static unsigned int
964 packed_size (typename std::vector<buffer_type>::const_iterator in)
965 {
966 // One entry for size, then pack the data tightly.
967 return 1 + (*in+T_per_buffer_type-1)/T_per_buffer_type;
968 }
969
970 static unsigned int packable_size
971 (const std::basic_string<T> & s,
972 const void *)
973 {
974 return 1 + (s.size()+T_per_buffer_type-1)/T_per_buffer_type;
975 }
976
977
978 template <typename Iter>
979 static void pack (const std::basic_string<T> & b, Iter data_out,
980 const void *)
981 {
982 *data_out++ = b.size();
983 std::size_t i = 0;
984 for (; i + T_per_buffer_type < b.size(); i += T_per_buffer_type)
985 *data_out++ = *reinterpret_cast<const buffer_type *>(&b[i]);
986 if (i != b.size())
987 {
988 T with_padding[T_per_buffer_type] = {};
989 std::copy(b.begin()+i, b.end(), &with_padding[0]);
990 *data_out++ = *reinterpret_cast<const buffer_type *>(&with_padding[0]);
991 }
992 }
993
994 static std::basic_string<T>
995 unpack (typename std::vector<buffer_type>::const_iterator in, void *)
996 {
997 const unsigned int string_len = *in++;
998
999 const T * buf =
1000 reinterpret_cast<const T *>(&(*in));
1001 in += string_len / T_per_buffer_type;
1002 return {buf, buf+string_len};
1003 }
1004
1005};
1006
1007} // namespace Parallel
1008
1009} // namespace libMesh
1010
1011
1012namespace TIMPI {
1013
1015
1016// ------------------------------------------------------------
1017// Packing member functions, global functions
1018
1022template <typename Context, typename Iter>
1023inline std::size_t packed_range_size (const Context * context,
1025 const Iter range_end)
1026{
1027 typedef typename std::iterator_traits<Iter>::value_type T;
1028
1029 std::size_t buffer_size = 0;
1032 ++range_count)
1033 {
1035 }
1036 return buffer_size;
1037}
1038
1039
1043template <typename Context, typename buffertype, typename Iter>
1046 const Iter range_end,
1047 std::vector<buffertype> & buffer,
1048 // When we serialize into buffers, we need to use large buffers to optimize MPI
1049 // bandwidth, but not so large as to risk allocation failures. max_buffer_size
1050 // is measured in number of buffer type entries; number of bytes may be 4 or 8
1051 // times larger depending on configuration.
1052 std::size_t approx_buffer_size)
1053{
1054 typedef typename std::iterator_traits<Iter>::value_type T;
1055
1056 // Count the total size of and preallocate buffer for efficiency.
1057 // Prepare to stop early if the buffer would be too large.
1058 std::size_t buffer_size = 0;
1061 ++range_stop)
1062 {
1063 std::size_t next_buffer_size =
1066 }
1067 buffer.reserve(buffer.size() + buffer_size);
1068
1069 // Pack the objects into the buffer
1070 for (; range_begin != range_stop; ++range_begin)
1071 {
1072#ifndef NDEBUG
1073 std::size_t old_size = buffer.size();
1074#endif
1075
1077 (*range_begin, std::back_inserter(buffer), context);
1078
1079#ifndef NDEBUG
1080 unsigned int my_packable_size =
1082 unsigned int my_packed_size =
1086#endif
1087 }
1088
1089 return range_stop;
1090}
1091
1092
1093
1101template <typename Context, typename buffertype,
1102 typename OutputIter, typename T>
1103inline OutputIter unpack_range (const std::vector<buffertype> & buffer,
1104 Context * context,
1106 const T * /* output_type */)
1107{
1108 // Loop through the buffer and unpack each object, returning the
1109 // object pointer via the output iterator
1110 typename std::vector<buffertype>::const_iterator
1111 next_object_start = buffer.begin();
1112
1113 while (next_object_start < buffer.end())
1114 {
1118 }
1119
1120 // We should have used up the exact amount of data in the buffer
1122
1123 return out_iter;
1124}
1125
1126} // namespace TIMPI
1127
1128#endif // TIMPI_PACKING_H
Templated class to provide the appropriate MPI datatype for use with built-in C types or simple C++ c...
static void pack(const Container &a, OutputIter data_out, const Context *context)
Definition packing.h:848
DefaultBufferType< typenameContainer::value_type >::type buffer_type
Definition packing.h:805
PackingMixedType< buffer_type > Mixed
Definition packing.h:807
static unsigned int packable_size(const Container &a, const Context *context)
Definition packing.h:828
static Container unpack(BufferIter in, Context *ctx)
Definition packing.h:863
static unsigned int packed_size(BufferIter iter)
Definition packing.h:839
static void pack(const std::array< T, N > &a, OutputIter data_out, const Context *context)
static std::basic_string< T > unpack(typename std::vector< buffer_type >::const_iterator in, void *)
Definition packing.h:995
static void pack(const std::pair< T1, T2 > &pr, OutputIter data_out, const Context *context)
static unsigned int packable_size(const std::pair< T1, T2 > &pr, const Context *context)
PairBufferTypeHelper< cT1, Has_buffer_type< Packing< cT1 > >::value, T2, Has_buffer_type< Packing< T2 > >::value >::buffer_type buffer_type
Definition packing.h:399
static void pack(const std::tuple< T, Types... > &tup, OutputIter data_out, const Context *context)
static unsigned int packable_size(const std::tuple< T, Types... > &tup, const Context *context)
Define data types and (un)serialization functions for use when encoding a potentially-variable-size o...
Definition packing.h:60
static T unpack(BufferIter in, Context *ctx)
static unsigned int packed_size(BufferIter iter)
Definition packing.h:433
static void pack(const T &object, OutputIter data_out, const Context *context)
static unsigned int packable_size(const T &object, const Context *context)
Iter pack_range(const Context *context, Iter range_begin, const Iter range_end, std::vector< buffertype > &buffer, std::size_t approx_buffer_size)
Helper function for range packing.
Definition packing.h:1044
Tnew cast_int(Told oldvar)
std::size_t packed_range_size(const Context *context, Iter range_begin, const Iter range_end)
Helper function for range packing.
Definition packing.h:1023
OutputIter unpack_range(const std::vector< buffertype > &buffer, Context *context, OutputIter out_iter, const T *)
Helper function for range unpacking.
Definition packing.h:1103
constexpr int get_packed_len_entries()
Definition packing.h:105
unsigned int get_packed_len(typename std::vector< buffer_type >::const_iterator in)
Definition packing.h:157
void put_packed_len(unsigned int len, Iter data_out)
Definition packing.h:116
TIMPI_PACKING_RANGE_SUBCLASS(std::vector< T TIMPI_P_COMMA A >)
static void pack_comp(const T3 &comp, OutputIter data_out, const Context *)
Definition packing.h:268
static void pack_comp(const std::pair< T1, T2 > &comp, OutputIter data_out, const Context *ctx)
Definition packing.h:285
static unsigned int packable_size_comp(const T3 &comp, const Context *ctx)
Definition packing.h:250
static void unpack_comp(T3 &comp, BufferIter in, Context *)
Definition packing.h:309
static void unpack_comp(std::pair< T1, T2 > &comp, BufferIter in, Context *ctx)
Definition packing.h:334
static unsigned int packable_size_comp(const T3 &, const Context *)
Definition packing.h:232
static unsigned int packable_size_comp(const std::pair< T1, T2 > &comp, const Context *ctx)
Definition packing.h:241
static void unpack_comp(T3 &comp, BufferIter in, Context *ctx)
Definition packing.h:348
static void pack_comp(const T3 &comp, OutputIter data_out, const Context *ctx)
Definition packing.h:295
Packing< std::tuple< MoreTypes... > >::buffer_type buffer_type
Definition packing.h:513
TupleBufferTypeHelper< Has_buffer_type< Packing< T1 > >::value, Has_buffer_type< Packing< std::tuple< MoreTypes... > > >::value, T1, MoreTypes... >::buffer_type buffer_type
Definition packing.h:533
Packing< T1 >::buffer_type buffer_type
Definition packing.h:523