TIMPI
Loading...
Searching...
No Matches
packed_range_unit.C
Go to the documentation of this file.
2#include <timpi/timpi.h>
3#include <timpi/packing.h>
4
5#include <iterator>
6#include <map>
7#include <set>
8#include <string>
9#include <vector>
10#include <memory>
11
12#define TIMPI_UNIT_ASSERT(expr) \
13 if (!(expr)) \
14 timpi_error()
15
16std::string stringy_number(int number)
17{
18 std::string digit_strings [10] = {"zero", "one", "two",
19 "three", "four", "five", "six", "seven", "eight", "nine"};
20
21 std::string returnval = "done";
22 while (number)
23 {
25 number = number/10;
26 };
27
28 return returnval;
29}
30
31
32std::vector<char> fake_stringy_number(int number)
33{
34 const std::string returnval = stringy_number(number);
35 return std::vector<char>(returnval.begin(), returnval.end());
36}
37
38template <typename T>
40{
41 using iterator_category = std::output_iterator_tag;
42 using value_type = T;
43 using difference_type = std::ptrdiff_t;
44 using pointer = T*;
45 using reference = T&;
46
47 template <typename T2>
48 void operator=(const T2&) {}
49
51 return *this;
52 }
53
57
58 // We don't return a reference-to-T here because we don't want to
59 // construct one or have any of its methods called.
60 null_output_iterator & operator*() { return *this; }
61};
62
63
64template <int i, typename T>
65inline
66auto my_get(T & container) -> decltype(std::get<i>(container))
67{
68 return std::get<i>(container);
69}
70
71
72template <int i, typename T>
73inline
74auto my_get(T & container) -> decltype(*container.begin())
75{
76 auto fwd_it = container.begin();
77 for (int n_it = 0; n_it < i; ++n_it)
78 ++fwd_it;
79 return *fwd_it;
80}
81
82
83// Need to disambiguate - array has get() and begin()...
84template <int i, typename T, std::size_t N>
85inline
86auto my_get(std::array<T, N> & container) -> decltype(std::get<i>(container))
87{
88 return std::get<i>(container);
89}
90
91
92template <typename T>
93inline void my_resize(T & container, std::size_t size) { container.resize(size); }
94
95template <typename T, typename U>
96inline void my_resize(std::pair<T,U> &, std::size_t) {}
97
98template <typename ... Types>
99inline void my_resize(std::tuple<Types...> &, std::size_t) {}
100
101template <typename T, std::size_t N>
102inline void my_resize(std::array<T,N> &, std::size_t) {}
103
104
105#if __cplusplus > 201402L
106namespace libMesh
107{
108namespace Parallel
109{
110template <>
111class Packing<std::unique_ptr<int>>
112{
113public:
114 typedef int buffer_type;
115
116 static unsigned int packed_size(typename std::vector<int>::const_iterator) { return 1; }
117 static unsigned int packable_size(const std::unique_ptr<int> &, const void *) { return 1; }
118
119 template <typename Iter>
120 static void pack(const std::unique_ptr<int> & object, Iter data_out, const void *) { data_out = *object; }
121
122 template <typename BufferIter>
123 static std::unique_ptr<int> unpack(BufferIter in, const void *)
124 {
125 return std::make_unique<int>(*in++);
126 }
127};
128}
129}
130#endif
131
132using namespace TIMPI;
133
135
137 {
138 std::vector<std::string> send(1);
139 if (TestCommWorld->rank() == 0)
140 send[0].assign("Hello");
141 else
142 send[0].assign("Goodbye");
143
145 ((void *)(NULL), send.begin(), send.end(),
147 }
148
149
150 // Make sure we don't have problems with strings of length above 256
151 // inside pairs, like we used to.
152 template <typename PairAtLeast>
154 {
155 std::vector<PairAtLeast> sendv(2);
156 my_resize(sendv[0], 2);
157 my_resize(sendv[1], 2);
158
159 my_get<0>(sendv[0]).assign("Hello");
160 auto & s0 = my_get<1>(sendv[0]);
161 s0.assign("Is it me you're looking for?\n");
162 for (int i=0; i != 6; ++i)
163 s0 = s0+s0;
164 timpi_assert_greater(s0.size(), 256);
165
166 my_get<0>(sendv[1]).assign("Goodbye");
167 auto & s1 = my_get<1>(sendv[1]);
168 s1.assign("to you! Guess it's better to say, goodbye\n");
169 for (int i=0; i != 6; ++i)
170 s1 = s1+s1;
171 timpi_assert_greater(s1.size(), 256);
172
173 std::vector<PairAtLeast> send(1);
174 my_resize(send[0], 2);
175
176 if (TestCommWorld->rank() == 0)
177 send[0] = sendv[0];
178 else
179 send[0] = sendv[1];
180
181 std::vector<PairAtLeast> recv;
182
184 ((void *)(NULL), send.begin(), send.end(),
185 std::back_inserter(recv));
186
187 const std::size_t comm_size = TestCommWorld->size();
188 const std::size_t vec_size = recv.size();
190
191 TIMPI_UNIT_ASSERT(sendv[0] == recv[0]);
192 for (std::size_t i=1; i < vec_size; ++i)
193 TIMPI_UNIT_ASSERT(sendv[1] == recv[i]);
194 }
195
196
201
202
207
208
213
214
219
220
221 // Make sure we don't have problems with strings of length above 256
222 // inside other containers either. Also test mixing strings with
223 // other types.
225 {
226#ifdef TIMPI_HAVE_CXX17 // static_assert without message
227 static_assert(Has_buffer_type<Packing<std::string>>::value);
234#endif
235
236 std::vector<std::tuple<std::string, std::string, int>> sendv(2);
237
238 auto & s0 = std::get<1>(sendv[0]);
239 std::get<0>(sendv[0]).assign("Hello");
240 s0.assign("Is it me you're looking for?\n");
241 for (int i=0; i != 6; ++i)
242 s0 = s0+s0;
243 timpi_assert_greater(s0.size(), 256);
244 std::get<2>(sendv[0]) = 257;
245
246 auto & s1 = std::get<1>(sendv[1]);
247 std::get<0>(sendv[1]).assign("Goodbye");
248 s1.assign("to you! Guess it's better to say, goodbye\n");
249 for (int i=0; i != 6; ++i)
250 s1 = s1+s1;
251 timpi_assert_greater(s1.size(), 256);
252 std::get<2>(sendv[1]) = 258;
253
254 std::vector<std::tuple<std::string, std::string, int>> send(1);
255 if (TestCommWorld->rank() == 0)
256 send[0] = sendv[0];
257 else
258 send[0] = sendv[1];
259
260 std::vector<std::tuple<std::string, std::string, int>> recv;
261
263 ((void *)(NULL), send.begin(), send.end(),
264 std::back_inserter(recv));
265
266 const std::size_t comm_size = TestCommWorld->size();
267 const std::size_t vec_size = recv.size();
269
270 TIMPI_UNIT_ASSERT(sendv[0] == recv[0]);
271 for (std::size_t i=1; i < vec_size; ++i)
272 TIMPI_UNIT_ASSERT(sendv[1] == recv[i]);
273 }
274
275
276
277 // We should be able to nest containers in other containers in the
278 // first containers etc.
280 {
281 typedef std::tuple<unsigned int, std::vector<std::tuple<char,int,std::size_t>>, unsigned int> send_type;
282
283#ifdef TIMPI_HAVE_CXX17 // static_assert without message
285 static_assert(Has_buffer_type<Packing<send_type>>::value);
286#endif
287
288 std::vector<send_type> sendv(2);
289
290 std::get<0>(sendv[0]) = 100;
291 std::get<1>(sendv[0]) = {{'a', -4, 1000},{'b', -5, 2000}};
292 std::get<2>(sendv[0]) = 3000;
293
294 std::get<0>(sendv[1]) = 200;
295 std::get<1>(sendv[1]) = {{'c', -6, 4000},{'d', -7, 5000}};
296 std::get<2>(sendv[1]) = 6000;
297
298 std::vector<send_type> send(1);
299 if (TestCommWorld->rank() == 0)
300 send[0] = sendv[0];
301 else
302 send[0] = sendv[1];
303
304 std::vector<send_type> recv;
305
307 ((void *)(NULL), send.begin(), send.end(),
308 std::back_inserter(recv));
309
310 const std::size_t comm_size = TestCommWorld->size();
311 const std::size_t vec_size = recv.size();
313
314 TIMPI_UNIT_ASSERT(sendv[0] == recv[0]);
315 for (std::size_t i=1; i < vec_size; ++i)
316 TIMPI_UNIT_ASSERT(sendv[1] == recv[i]);
317 }
318
319
320
322 {
323 std::vector<std::string> send(1);
324 const unsigned int my_rank = TestCommWorld->rank();
325 const unsigned int dest_rank =
326 (my_rank + 1) % TestCommWorld->size();
327 const unsigned int source_rank =
329
330 {
331 std::ostringstream os;
332 os << my_rank;
333 send[0] = os.str();
334 }
335
337 (dest_rank, (void *)(NULL), send.begin(), send.end(),
338 source_rank, (void *)(NULL),
340 (std::string*)NULL);
341 }
342
344 {
345 // This method uses a specialized allgather method that is only defined
346 // when we have MPI
347#ifdef TIMPI_HAVE_MPI
348 std::vector<std::string> vals;
349 const unsigned int my_rank = TestCommWorld->rank();
350 TestCommWorld->allgather(std::string(my_rank+1, '0' + my_rank), vals);
351
352 const std::size_t comm_size = TestCommWorld->size();
353 const std::size_t vec_size = vals.size();
355
356 for (std::size_t i = 0; i < vec_size; ++i)
357 TIMPI_UNIT_ASSERT(vals[i] == std::string(i + 1, '0' + i));
358#endif
359 }
360
362 {
363 std::vector<processor_id_type> vals;
364
365 std::vector<std::string> send(1), recv;
366
367 const unsigned int my_rank = TestCommWorld->rank();
368 const unsigned int dest_rank =
369 (my_rank + 1) % TestCommWorld->size();
370 const unsigned int source_rank =
372
373 {
374 std::ostringstream os;
375 os << my_rank;
376 send[0] = os.str();
377 }
378
380 (dest_rank, (void *)(NULL), send.begin(), send.end(),
381 source_rank, (void *)(NULL),
382 std::back_inserter(recv),
383 (std::string*)NULL);
384
385 TIMPI_UNIT_ASSERT(recv.size() == std::size_t(1));
386
387 std::string check;
388 {
389 std::ostringstream os;
390 os << source_rank;
391 check = os.str();
392 }
393
394 TIMPI_UNIT_ASSERT(recv[0] == check);
395 }
396
398 {
399 // Number of entries in the map on each processor
400 const unsigned int n_map_entries = 147;
401
402 // Offset between blocks of entries; must exceed n_map_entries
403 const unsigned int map_offset = 1000;
404
405 // Large enough strings that we'll exceed a single packed buffer
406 const unsigned int string_size = 32768;
407
408 // Each entry in the map will be the same test_string
409 std::string test_string(/*count*/string_size, 'a');
410
411 std::map<unsigned int, std::string> map_of_strings;
412
413 // Use an offset so that the map keys do not overlap
414 const unsigned int my_offset =
415 (TestCommWorld->rank() + 1) * map_offset;
416 for (unsigned int i=0; i != n_map_entries; ++i)
418
420
423
425 }
426
427
429 {
430 const int size = TestCommWorld->size(),
431 rank = TestCommWorld->rank();
432
433 std::map<processor_id_type, std::multiset<std::string>>
435
436 auto stringy_number = [] (int number)
437 {
438 std::string digit_strings [10] = {"zero", "one", "two",
439 "three", "four", "five", "six", "seven", "eight", "nine"};
440
441 std::string returnval = "done";
442 while (number)
443 {
445 number = number/10;
446 };
447
448 return returnval;
449 };
450
451 for (int d=0; d != M; ++d)
452 {
453 int diffsize = std::abs(d-rank);
454 int diffsqrt = std::sqrt(diffsize);
456 for (int i=-1; i != diffsqrt; ++i)
458 }
459
460 auto collect_data =
463 const typename std::multiset<std::string> & multiset_received)
464 {
465 auto & received = received_data[pid];
466 received.insert(multiset_received.begin(), multiset_received.end());
467 };
468
469 // Ensure that no const_cast perfidy in parallel_sync.h messes up
470 // our original data
471 std::map<processor_id_type, std::multiset<std::string>> preserved_data {data};
472
473 // Do the push
474 void * context = nullptr;
476
477 // Test the sent data, which shouldn't have changed
478 TIMPI_UNIT_ASSERT(preserved_data.size() == data.size());
479 for (const auto & pair: preserved_data)
480 {
481 const auto &pd_ms = pair.second;
482 const auto &d_ms = data[pair.first];
483 TIMPI_UNIT_ASSERT(pd_ms.size() == d_ms.size());
484 for (auto entry : pd_ms)
485 TIMPI_UNIT_ASSERT(pd_ms.count(entry) == d_ms.count(entry));
486 }
487
488 // Test the received results, for each processor id p we're in
489 // charge of.
490 std::vector<std::size_t> checked_sizes(size, 0);
491 for (int p=rank; p < M; p += size)
492 for (int srcp=0; srcp != size; ++srcp)
493 {
494 int diffsize = std::abs(srcp-p);
495 int diffsqrt = std::sqrt(diffsize);
497 {
498 if (received_data.count(srcp))
499 {
500 const std::multiset<std::string> & datum = received_data[srcp];
502 (std::count(datum.begin(), datum.end(),
503 stringy_number(p)) == std::ptrdiff_t(0));
504 }
505 continue;
506 }
507
508 TIMPI_UNIT_ASSERT(received_data.count(srcp) == std::size_t(1));
509 const std::multiset<std::string> & datum = received_data[srcp];
511 (std::count(datum.begin(), datum.end(), stringy_number(p)) ==
512 std::ptrdiff_t(diffsqrt+1));
514 }
515
516 for (int srcp=0; srcp != size; ++srcp)
518
519 }
520
525
527 {
529 }
530
531 template <typename FillFunctor, typename PushFunctor>
533 {
534 const int size = TestCommWorld->size(),
535 rank = TestCommWorld->rank();
536
537 typedef decltype(fill_functor(0)) fill_type;
538
539 typedef std::vector<fill_type> vec_type;
540 std::map<processor_id_type, vec_type> data, received_data;
541
542 for (int d=0; d != size; ++d)
543 {
544 int diffsize = std::abs(d-rank);
545 int diffsqrt = std::sqrt(diffsize);
547 for (int i=-1; i != diffsqrt; ++i)
549 }
550
551 auto collect_data =
554 const vec_type & vec_received)
555 {
556 auto & received = received_data[pid];
558 };
559
560 // Ensure that no const_cast perfidy in parallel_sync.h messes up
561 // our original data
562 std::map<processor_id_type, vec_type> preserved_data {data};
563
564 // Do the push
566
567 // Test the sent data, which shouldn't have changed
569
570 // Test the received results, for each processor id p we're in
571 // charge of.
572 std::vector<std::size_t> checked_sizes(size, 0);
573 for (int srcp=0; srcp != size; ++srcp)
574 {
575 int diffsize = std::abs(srcp-rank);
576 int diffsqrt = std::sqrt(diffsize);
578 {
580 continue;
581 }
582
584 const auto & rec = received_data[srcp];
585 TIMPI_UNIT_ASSERT(rec.size() == std::size_t(diffsqrt+1));
586
587 for (auto & tup : rec)
589 }
590 }
591
592
594 {
595 // Do the push explicitly with a packed_range function
596 auto explicitly_packed_push = [](auto & data, auto & collect_data) {
597 void * context = nullptr;
599 };
600
601 typedef std::tuple<unsigned int, std::vector<char>, unsigned int,
602 unsigned int, unsigned int, unsigned int> tuple_type;
603 auto fill_tuple= [] (int n) {
604 return tuple_type(n, fake_stringy_number(n), n, n, n, n);
605 };
606
608 }
609
610
612 {
613 // Do the push implicitly with an auto-dispatching function
614 auto implicitly_packed_push = [](auto & data, auto & collect_data) {
616 };
617
618 typedef std::tuple<unsigned int, std::vector<char>, unsigned int,
619 unsigned int, unsigned int, unsigned int> tuple_type;
620 auto fill_tuple= [] (int n) {
621 return tuple_type(n, fake_stringy_number(n), n, n, n, n);
622 };
623
625 }
626
627
629 {
630 typedef std::tuple<std::unordered_map<unsigned int, std::string>> tuple_type;
631#ifdef TIMPI_HAVE_CXX17 // static_assert without message
632 static_assert(Has_buffer_type<Packing<std::string>>::value);
642 static_assert(Has_buffer_type<Packing<tuple_type>>::value);
643#endif
644
645 auto fill_tuple = [] (int n)
646 {
648 (std::get<0>(returnval))[n] = stringy_number(n);
649 (std::get<0>(returnval))[n+1] = stringy_number(n+1);
650 return returnval;
651 };
652
653 auto implicitly_packed_push = [](auto & data, auto & collect_data) {
655 };
656
658 }
659
660
661// A failure case I caught downstream
663 {
664 typedef std::tuple<std::size_t, int, std::unordered_map<unsigned int, std::string>> tuple_type;
665
666 auto fill_tuple = [] (int n)
667 {
669 std::get<0>(returnval) = n;
670 std::get<1>(returnval) = n;
671 (std::get<2>(returnval))[n] = stringy_number(n);
672 (std::get<2>(returnval))[n+1] = stringy_number(n+1);
673 return returnval;
674 };
675
676 auto implicitly_packed_push = [](auto & data, auto & collect_data) {
678 };
679
681 }
682
683
684#if __cplusplus > 201402L
686 {
687 const int size = TestCommWorld->size(),
688 rank = TestCommWorld->rank();
689
690 std::map<processor_id_type, std::vector<std::unique_ptr<int>>>
692
693 for (int d=0; d != M; ++d)
694 {
695 int diffsize = std::abs(d-rank);
696 int diffsqrt = std::sqrt(diffsize);
698 for (int i=-1; i != diffsqrt; ++i)
699 data[d].emplace_back(std::make_unique<int>(d));
700 }
701
702 auto collect_data =
705 std::vector<std::unique_ptr<int>> && vector_received)
706 {
707 auto & received = received_data[pid];
708 for (auto & val : vector_received)
709 received.emplace_back(std::move(val));
710 };
711
712 void * context = nullptr;
714
715 // Test the received results, for each processor id p we're in
716 // charge of.
717 std::vector<std::size_t> checked_sizes(size, 0);
718 for (int p=rank; p < M; p += size)
719 for (int srcp=0; srcp != size; ++srcp)
720 {
721 int diffsize = std::abs(srcp-p);
722 int diffsqrt = std::sqrt(diffsize);
724 {
725 if (received_data.count(srcp))
726 {
727 std::size_t count = 0;
728 for (const auto & val : received_data[srcp])
729 if (*val == p)
730 ++count;
731
732 TIMPI_UNIT_ASSERT(count == (std::size_t)std::ptrdiff_t(0));
733 }
734 continue;
735 }
736
737 TIMPI_UNIT_ASSERT(received_data.count(srcp) == std::size_t(1));
738
739 std::size_t count = 0;
740 for (const auto & val : received_data[srcp])
741 if (*val == p)
742 ++count;
743
744 TIMPI_UNIT_ASSERT(count == (std::size_t)std::ptrdiff_t(diffsqrt+1));
746 }
747
748 for (int srcp=0; srcp != size; ++srcp)
750 }
751
756
761#endif
762
Encapsulates the MPI_Comm object.
void allgather_packed_range(Context *context, Iter range_begin, const Iter range_end, OutputIter out, std::size_t approx_buffer_size=1000000) const
Take a range of local variables, combine it with ranges from all processors, and write the output to ...
processor_id_type size() const
processor_id_type rank() const
void set_union(T &data, const unsigned int root_id) const
Take a container (set, map, unordered_set, multimap, etc) of local variables on each processor,...
void sync_type(const SyncType st)
Explicitly sets the SyncType used for sync operations.
void send_receive_packed_range(const unsigned int dest_processor_id, const Context1 *context1, RangeIter send_begin, const RangeIter send_end, const unsigned int source_processor_id, Context2 *context2, OutputIter out, const T *output_type, const MessageTag &send_tag=no_tag, const MessageTag &recv_tag=any_tag, std::size_t approx_buffer_size=1000000) const
Send a range-of-pointers to one processor while simultaneously receiving another range from a (potent...
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
Take a vector of length this->size(), and fill in recv[processor_id] = the value of send on that proc...
Templated class to provide the appropriate MPI datatype for use with built-in C types or simple C++ c...
The TIMPIInit class, when constructed, initializes any dependent libraries (e.g.
Definition timpi_init.h:58
static unsigned int packed_size(typename std::vector< int >::const_iterator)
static void pack(const std::unique_ptr< int > &object, Iter data_out, const void *)
static std::unique_ptr< int > unpack(BufferIter in, const void *)
static unsigned int packable_size(const std::unique_ptr< int > &, const void *)
Define data types and (un)serialization functions for use when encoding a potentially-variable-size o...
Definition packing.h:60
uint8_t processor_id_type
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
Send and receive and act on vectors of data.
Tnew cast_int(Told oldvar)
void push_parallel_packed_range(const Communicator &comm, MapToVectors &&data, Context *context, const ActionFunctor &act_on_data)
Send and receive and act on vectors of data.
void testPushPackedNestedImpl(FillFunctor fill_functor, PushFunctor push_functor)
void testContainerSendReceive()
void testPushPackedNested()
std::vector< char > fake_stringy_number(int number)
Communicator * TestCommWorld
void testGettableStringAllGather()
void testPushPackedImplMove(int M)
auto my_get(T &container) -> decltype(std::get< i >(container))
std::string stringy_number(int number)
void testArrayStringAllGather()
void testTupleStringAllGather()
void testPushPacked()
void testPushPackedOneTuple()
void testPushPackedMoveOversized()
void testPushPackedFailureCase()
void testNullSendReceive()
void testPushPackedOversized()
void testNestingAllGather()
void testPairStringAllGather()
void testListStringAllGather()
void testPushPackedMove()
void my_resize(T &container, std::size_t size)
void testPushPackedImpl(int M)
void testNullAllGather()
void testContainerAllGather()
void testLargeSetUnion()
void testPushPackedDispatch()
void testVectorStringAllGather()
null_output_iterator operator++(int)
null_output_iterator & operator++()
std::ptrdiff_t difference_type
void operator=(const T2 &)
std::output_iterator_tag iterator_category
null_output_iterator & operator*()
int main()
Definition version.C:20