comparison DEPENDENCIES/generic/include/boost/mpi/nonblocking.hpp @ 16:2665513ce2d3

Add boost headers
author Chris Cannam
date Tue, 05 Aug 2014 11:11:38 +0100
parents
children c530137014c0
comparison
equal deleted inserted replaced
15:663ca0da4350 16:2665513ce2d3
1 // Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>.
2
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6
7 /** @file nonblocking.hpp
8 *
9 * This header defines operations for completing non-blocking
10 * communication requests.
11 */
12 #ifndef BOOST_MPI_NONBLOCKING_HPP
13 #define BOOST_MPI_NONBLOCKING_HPP
14
15 #include <boost/mpi/config.hpp>
16 #include <vector>
17 #include <iterator> // for std::iterator_traits
18 #include <boost/optional.hpp>
19 #include <utility> // for std::pair
20 #include <algorithm> // for iter_swap, reverse
21 #include <boost/static_assert.hpp>
22 #include <boost/mpi/request.hpp>
23 #include <boost/mpi/status.hpp>
24 #include <boost/mpi/exception.hpp>
25
26 namespace boost { namespace mpi {
27
28 /**
29 * @brief Wait until any non-blocking request has completed.
30 *
31 * This routine takes in a set of requests stored in the iterator
32 * range @c [first,last) and waits until any of these requests has
33 * been completed. It provides functionality equivalent to
34 * @c MPI_Waitany.
35 *
36 * @param first The iterator that denotes the beginning of the
37 * sequence of request objects.
38 *
39 * @param last The iterator that denotes the end of the sequence of
40 * request objects. This may not be equal to @c first.
41 *
42 * @returns A pair containing the status object that corresponds to
43 * the completed operation and the iterator referencing the completed
44 * request.
45 */
46 template<typename ForwardIterator>
47 std::pair<status, ForwardIterator>
48 wait_any(ForwardIterator first, ForwardIterator last)
49 {
50 using std::advance;
51
52 BOOST_ASSERT(first != last);
53
54 typedef typename std::iterator_traits<ForwardIterator>::difference_type
55 difference_type;
56
57 bool all_trivial_requests = true;
58 difference_type n = 0;
59 ForwardIterator current = first;
60 while (true) {
61 // Check if we have found a completed request. If so, return it.
62 if (optional<status> result = current->test())
63 return std::make_pair(*result, current);
64
65 // Check if this request (and all others before it) are "trivial"
66 // requests, e.g., they can be represented with a single
67 // MPI_Request.
68 all_trivial_requests =
69 all_trivial_requests
70 && !current->m_handler
71 && current->m_requests[1] == MPI_REQUEST_NULL;
72
73 // Move to the next request.
74 ++n;
75 if (++current == last) {
76 // We have reached the end of the list. If all requests thus far
77 // have been trivial, we can call MPI_Waitany directly, because
78 // it may be more efficient than our busy-wait semantics.
79 if (all_trivial_requests) {
80 std::vector<MPI_Request> requests;
81 requests.reserve(n);
82 for (current = first; current != last; ++current)
83 requests.push_back(current->m_requests[0]);
84
85 // Let MPI wait until one of these operations completes.
86 int index;
87 status stat;
88 BOOST_MPI_CHECK_RESULT(MPI_Waitany,
89 (n, &requests[0], &index, &stat.m_status));
90
91 // We don't have a notion of empty requests or status objects,
92 // so this is an error.
93 if (index == MPI_UNDEFINED)
94 boost::throw_exception(exception("MPI_Waitany", MPI_ERR_REQUEST));
95
96 // Find the iterator corresponding to the completed request.
97 current = first;
98 advance(current, index);
99 current->m_requests[0] = requests[index];
100 return std::make_pair(stat, current);
101 }
102
103 // There are some nontrivial requests, so we must continue our
104 // busy waiting loop.
105 n = 0;
106 current = first;
107 all_trivial_requests = true;
108 }
109 }
110
111 // We cannot ever get here
112 BOOST_ASSERT(false);
113 }
114
115 /**
116 * @brief Test whether any non-blocking request has completed.
117 *
118 * This routine takes in a set of requests stored in the iterator
119 * range @c [first,last) and tests whether any of these requests has
120 * been completed. This routine is similar to @c wait_any, but will
121 * not block waiting for requests to completed. It provides
122 * functionality equivalent to @c MPI_Testany.
123 *
124 * @param first The iterator that denotes the beginning of the
125 * sequence of request objects.
126 *
127 * @param last The iterator that denotes the end of the sequence of
128 * request objects.
129 *
130 * @returns If any outstanding requests have completed, a pair
131 * containing the status object that corresponds to the completed
132 * operation and the iterator referencing the completed
133 * request. Otherwise, an empty @c optional<>.
134 */
135 template<typename ForwardIterator>
136 optional<std::pair<status, ForwardIterator> >
137 test_any(ForwardIterator first, ForwardIterator last)
138 {
139 for (ForwardIterator current = first; first != last; ++first) {
140 // Check if we have found a completed request. If so, return it.
141 if (optional<status> result = current->test())
142 return std::make_pair(*result, current);
143 }
144
145 // We found nothing
146 return optional<std::pair<status, ForwardIterator> >();
147 }
148
149 /**
150 * @brief Wait until all non-blocking requests have completed.
151 *
152 * This routine takes in a set of requests stored in the iterator
153 * range @c [first,last) and waits until all of these requests have
154 * been completed. It provides functionality equivalent to
155 * @c MPI_Waitall.
156 *
157 * @param first The iterator that denotes the beginning of the
158 * sequence of request objects.
159 *
160 * @param last The iterator that denotes the end of the sequence of
161 * request objects.
162 *
163 * @param out If provided, an output iterator through which the
164 * status of each request will be emitted. The @c status objects are
165 * emitted in the same order as the requests are retrieved from
166 * @c [first,last).
167 *
168 * @returns If an @p out parameter was provided, the value @c out
169 * after all of the @c status objects have been emitted.
170 */
171 template<typename ForwardIterator, typename OutputIterator>
172 OutputIterator
173 wait_all(ForwardIterator first, ForwardIterator last, OutputIterator out)
174 {
175 typedef typename std::iterator_traits<ForwardIterator>::difference_type
176 difference_type;
177
178 using std::distance;
179
180 difference_type num_outstanding_requests = distance(first, last);
181
182 std::vector<status> results(num_outstanding_requests);
183 std::vector<bool> completed(num_outstanding_requests);
184
185 while (num_outstanding_requests > 0) {
186 bool all_trivial_requests = true;
187 difference_type idx = 0;
188 for (ForwardIterator current = first; current != last; ++current, ++idx) {
189 if (!completed[idx]) {
190 if (optional<status> stat = current->test()) {
191 // This outstanding request has been completed. We're done.
192 results[idx] = *stat;
193 completed[idx] = true;
194 --num_outstanding_requests;
195 all_trivial_requests = false;
196 } else {
197 // Check if this request (and all others before it) are "trivial"
198 // requests, e.g., they can be represented with a single
199 // MPI_Request.
200 all_trivial_requests =
201 all_trivial_requests
202 && !current->m_handler
203 && current->m_requests[1] == MPI_REQUEST_NULL;
204 }
205 }
206 }
207
208 // If we have yet to fulfill any requests and all of the requests
209 // are trivial (i.e., require only a single MPI_Request to be
210 // fulfilled), call MPI_Waitall directly.
211 if (all_trivial_requests
212 && num_outstanding_requests == (difference_type)results.size()) {
213 std::vector<MPI_Request> requests;
214 requests.reserve(num_outstanding_requests);
215 for (ForwardIterator current = first; current != last; ++current)
216 requests.push_back(current->m_requests[0]);
217
218 // Let MPI wait until all of these operations completes.
219 std::vector<MPI_Status> stats(num_outstanding_requests);
220 BOOST_MPI_CHECK_RESULT(MPI_Waitall,
221 (num_outstanding_requests, &requests[0],
222 &stats[0]));
223
224 for (std::vector<MPI_Status>::iterator i = stats.begin();
225 i != stats.end(); ++i, ++out) {
226 status stat;
227 stat.m_status = *i;
228 *out = stat;
229 }
230
231 return out;
232 }
233
234 all_trivial_requests = false;
235 }
236
237 return std::copy(results.begin(), results.end(), out);
238 }
239
240 /**
241 * \overload
242 */
243 template<typename ForwardIterator>
244 void
245 wait_all(ForwardIterator first, ForwardIterator last)
246 {
247 typedef typename std::iterator_traits<ForwardIterator>::difference_type
248 difference_type;
249
250 using std::distance;
251
252 difference_type num_outstanding_requests = distance(first, last);
253
254 std::vector<bool> completed(num_outstanding_requests);
255
256 while (num_outstanding_requests > 0) {
257 bool all_trivial_requests = true;
258
259 difference_type idx = 0;
260 for (ForwardIterator current = first; current != last; ++current, ++idx) {
261 if (!completed[idx]) {
262 if (optional<status> stat = current->test()) {
263 // This outstanding request has been completed.
264 completed[idx] = true;
265 --num_outstanding_requests;
266 all_trivial_requests = false;
267 } else {
268 // Check if this request (and all others before it) are "trivial"
269 // requests, e.g., they can be represented with a single
270 // MPI_Request.
271 all_trivial_requests =
272 all_trivial_requests
273 && !current->m_handler
274 && current->m_requests[1] == MPI_REQUEST_NULL;
275 }
276 }
277 }
278
279 // If we have yet to fulfill any requests and all of the requests
280 // are trivial (i.e., require only a single MPI_Request to be
281 // fulfilled), call MPI_Waitall directly.
282 if (all_trivial_requests
283 && num_outstanding_requests == (difference_type)completed.size()) {
284 std::vector<MPI_Request> requests;
285 requests.reserve(num_outstanding_requests);
286 for (ForwardIterator current = first; current != last; ++current)
287 requests.push_back(current->m_requests[0]);
288
289 // Let MPI wait until all of these operations completes.
290 BOOST_MPI_CHECK_RESULT(MPI_Waitall,
291 (num_outstanding_requests, &requests[0],
292 MPI_STATUSES_IGNORE));
293
294 // Signal completion
295 num_outstanding_requests = 0;
296 }
297 }
298 }
299
300 /**
301 * @brief Tests whether all non-blocking requests have completed.
302 *
303 * This routine takes in a set of requests stored in the iterator
304 * range @c [first,last) and determines whether all of these requests
305 * have been completed. However, due to limitations of the underlying
306 * MPI implementation, if any of the requests refers to a
307 * non-blocking send or receive of a serialized data type, @c
308 * test_all will always return the equivalent of @c false (i.e., the
309 * requests cannot all be finished at this time). This routine
310 * performs the same functionality as @c wait_all, except that this
311 * routine will not block. This routine provides functionality
312 * equivalent to @c MPI_Testall.
313 *
314 * @param first The iterator that denotes the beginning of the
315 * sequence of request objects.
316 *
317 * @param last The iterator that denotes the end of the sequence of
318 * request objects.
319 *
320 * @param out If provided and all requests hav been completed, an
321 * output iterator through which the status of each request will be
322 * emitted. The @c status objects are emitted in the same order as
323 * the requests are retrieved from @c [first,last).
324 *
325 * @returns If an @p out parameter was provided, the value @c out
326 * after all of the @c status objects have been emitted (if all
327 * requests were completed) or an empty @c optional<>. If no @p out
328 * parameter was provided, returns @c true if all requests have
329 * completed or @c false otherwise.
330 */
331 template<typename ForwardIterator, typename OutputIterator>
332 optional<OutputIterator>
333 test_all(ForwardIterator first, ForwardIterator last, OutputIterator out)
334 {
335 std::vector<MPI_Request> requests;
336 for (; first != last; ++first) {
337 // If we have a non-trivial request, then no requests can be
338 // completed.
339 if (first->m_handler || first->m_requests[1] != MPI_REQUEST_NULL)
340 return optional<OutputIterator>();
341
342 requests.push_back(first->m_requests[0]);
343 }
344
345 int flag = 0;
346 int n = requests.size();
347 std::vector<MPI_Status> stats(n);
348 BOOST_MPI_CHECK_RESULT(MPI_Testall, (n, &requests[0], &flag, &stats[0]));
349 if (flag) {
350 for (int i = 0; i < n; ++i, ++out) {
351 status stat;
352 stat.m_status = stats[i];
353 *out = stat;
354 }
355 return out;
356 } else {
357 return optional<OutputIterator>();
358 }
359 }
360
361 /**
362 * \overload
363 */
364 template<typename ForwardIterator>
365 bool
366 test_all(ForwardIterator first, ForwardIterator last)
367 {
368 std::vector<MPI_Request> requests;
369 for (; first != last; ++first) {
370 // If we have a non-trivial request, then no requests can be
371 // completed.
372 if (first->m_handler || first->m_requests[1] != MPI_REQUEST_NULL)
373 return false;
374
375 requests.push_back(first->m_requests[0]);
376 }
377
378 int flag = 0;
379 int n = requests.size();
380 BOOST_MPI_CHECK_RESULT(MPI_Testall,
381 (n, &requests[0], &flag, MPI_STATUSES_IGNORE));
382 return flag != 0;
383 }
384
385 /**
386 * @brief Wait until some non-blocking requests have completed.
387 *
388 * This routine takes in a set of requests stored in the iterator
389 * range @c [first,last) and waits until at least one of the requests
390 * has completed. It then completes all of the requests it can,
391 * partitioning the input sequence into pending requests followed by
392 * completed requests. If an output iterator is provided, @c status
393 * objects will be emitted for each of the completed requests. This
394 * routine provides functionality equivalent to @c MPI_Waitsome.
395 *
396 * @param first The iterator that denotes the beginning of the
397 * sequence of request objects.
398 *
399 * @param last The iterator that denotes the end of the sequence of
400 * request objects. This may not be equal to @c first.
401 *
402 * @param out If provided, the @c status objects corresponding to
403 * completed requests will be emitted through this output iterator.
404
405 * @returns If the @p out parameter was provided, a pair containing
406 * the output iterator @p out after all of the @c status objects have
407 * been written through it and an iterator referencing the first
408 * completed request. If no @p out parameter was provided, only the
409 * iterator referencing the first completed request will be emitted.
410 */
411 template<typename BidirectionalIterator, typename OutputIterator>
412 std::pair<OutputIterator, BidirectionalIterator>
413 wait_some(BidirectionalIterator first, BidirectionalIterator last,
414 OutputIterator out)
415 {
416 using std::advance;
417
418 if (first == last)
419 return std::make_pair(out, first);
420
421 typedef typename std::iterator_traits<BidirectionalIterator>::difference_type
422 difference_type;
423
424 bool all_trivial_requests = true;
425 difference_type n = 0;
426 BidirectionalIterator current = first;
427 BidirectionalIterator start_of_completed = last;
428 while (true) {
429 // Check if we have found a completed request.
430 if (optional<status> result = current->test()) {
431 using std::iter_swap;
432
433 // Emit the resulting status object
434 *out++ = *result;
435
436 // We're expanding the set of completed requests
437 --start_of_completed;
438
439 if (current == start_of_completed) {
440 // If we have hit the end of the list of pending
441 // requests. Finish up by fixing the order of the completed
442 // set to match the order in which we emitted status objects,
443 // then return.
444 std::reverse(start_of_completed, last);
445 return std::make_pair(out, start_of_completed);
446 }
447
448 // Swap the request we just completed with the last request that
449 // has not yet been tested.
450 iter_swap(current, start_of_completed);
451
452 continue;
453 }
454
455 // Check if this request (and all others before it) are "trivial"
456 // requests, e.g., they can be represented with a single
457 // MPI_Request.
458 all_trivial_requests =
459 all_trivial_requests
460 && !current->m_handler
461 && current->m_requests[1] == MPI_REQUEST_NULL;
462
463 // Move to the next request.
464 ++n;
465 if (++current == start_of_completed) {
466 if (start_of_completed != last) {
467 // We have satisfied some requests. Make the order of the
468 // completed requests match that of the status objects we've
469 // already emitted and we're done.
470 std::reverse(start_of_completed, last);
471 return std::make_pair(out, start_of_completed);
472 }
473
474 // We have reached the end of the list. If all requests thus far
475 // have been trivial, we can call MPI_Waitsome directly, because
476 // it may be more efficient than our busy-wait semantics.
477 if (all_trivial_requests) {
478 std::vector<MPI_Request> requests;
479 std::vector<int> indices(n);
480 std::vector<MPI_Status> stats(n);
481 requests.reserve(n);
482 for (current = first; current != last; ++current)
483 requests.push_back(current->m_requests[0]);
484
485 // Let MPI wait until some of these operations complete.
486 int num_completed;
487 BOOST_MPI_CHECK_RESULT(MPI_Waitsome,
488 (n, &requests[0], &num_completed, &indices[0],
489 &stats[0]));
490
491 // Translate the index-based result of MPI_Waitsome into a
492 // partitioning on the requests.
493 int current_offset = 0;
494 current = first;
495 for (int index = 0; index < num_completed; ++index, ++out) {
496 using std::iter_swap;
497
498 // Move "current" to the request object at this index
499 advance(current, indices[index] - current_offset);
500 current_offset = indices[index];
501
502 // Emit the status object
503 status stat;
504 stat.m_status = stats[index];
505 *out = stat;
506
507 // Finish up the request and swap it into the "completed
508 // requests" partition.
509 current->m_requests[0] = requests[indices[index]];
510 --start_of_completed;
511 iter_swap(current, start_of_completed);
512 }
513
514 // We have satisfied some requests. Make the order of the
515 // completed requests match that of the status objects we've
516 // already emitted and we're done.
517 std::reverse(start_of_completed, last);
518 return std::make_pair(out, start_of_completed);
519 }
520
521 // There are some nontrivial requests, so we must continue our
522 // busy waiting loop.
523 n = 0;
524 current = first;
525 }
526 }
527
528 // We cannot ever get here
529 BOOST_ASSERT(false);
530 }
531
532 /**
533 * \overload
534 */
535 template<typename BidirectionalIterator>
536 BidirectionalIterator
537 wait_some(BidirectionalIterator first, BidirectionalIterator last)
538 {
539 using std::advance;
540
541 if (first == last)
542 return first;
543
544 typedef typename std::iterator_traits<BidirectionalIterator>::difference_type
545 difference_type;
546
547 bool all_trivial_requests = true;
548 difference_type n = 0;
549 BidirectionalIterator current = first;
550 BidirectionalIterator start_of_completed = last;
551 while (true) {
552 // Check if we have found a completed request.
553 if (optional<status> result = current->test()) {
554 using std::iter_swap;
555
556 // We're expanding the set of completed requests
557 --start_of_completed;
558
559 // If we have hit the end of the list of pending requests, we're
560 // done.
561 if (current == start_of_completed)
562 return start_of_completed;
563
564 // Swap the request we just completed with the last request that
565 // has not yet been tested.
566 iter_swap(current, start_of_completed);
567
568 continue;
569 }
570
571 // Check if this request (and all others before it) are "trivial"
572 // requests, e.g., they can be represented with a single
573 // MPI_Request.
574 all_trivial_requests =
575 all_trivial_requests
576 && !current->m_handler
577 && current->m_requests[1] == MPI_REQUEST_NULL;
578
579 // Move to the next request.
580 ++n;
581 if (++current == start_of_completed) {
582 // If we have satisfied some requests, we're done.
583 if (start_of_completed != last)
584 return start_of_completed;
585
586 // We have reached the end of the list. If all requests thus far
587 // have been trivial, we can call MPI_Waitsome directly, because
588 // it may be more efficient than our busy-wait semantics.
589 if (all_trivial_requests) {
590 std::vector<MPI_Request> requests;
591 std::vector<int> indices(n);
592 requests.reserve(n);
593 for (current = first; current != last; ++current)
594 requests.push_back(current->m_requests[0]);
595
596 // Let MPI wait until some of these operations complete.
597 int num_completed;
598 BOOST_MPI_CHECK_RESULT(MPI_Waitsome,
599 (n, &requests[0], &num_completed, &indices[0],
600 MPI_STATUSES_IGNORE));
601
602 // Translate the index-based result of MPI_Waitsome into a
603 // partitioning on the requests.
604 int current_offset = 0;
605 current = first;
606 for (int index = 0; index < num_completed; ++index) {
607 using std::iter_swap;
608
609 // Move "current" to the request object at this index
610 advance(current, indices[index] - current_offset);
611 current_offset = indices[index];
612
613 // Finish up the request and swap it into the "completed
614 // requests" partition.
615 current->m_requests[0] = requests[indices[index]];
616 --start_of_completed;
617 iter_swap(current, start_of_completed);
618 }
619
620 // We have satisfied some requests, so we are done.
621 return start_of_completed;
622 }
623
624 // There are some nontrivial requests, so we must continue our
625 // busy waiting loop.
626 n = 0;
627 current = first;
628 }
629 }
630
631 // We cannot ever get here
632 BOOST_ASSERT(false);
633 }
634
635 /**
636 * @brief Test whether some non-blocking requests have completed.
637 *
638 * This routine takes in a set of requests stored in the iterator
639 * range @c [first,last) and tests to see if any of the requests has
640 * completed. It completes all of the requests it can, partitioning
641 * the input sequence into pending requests followed by completed
642 * requests. If an output iterator is provided, @c status objects
643 * will be emitted for each of the completed requests. This routine
644 * is similar to @c wait_some, but does not wait until any requests
645 * have completed. This routine provides functionality equivalent to
646 * @c MPI_Testsome.
647 *
648 * @param first The iterator that denotes the beginning of the
649 * sequence of request objects.
650 *
651 * @param last The iterator that denotes the end of the sequence of
652 * request objects. This may not be equal to @c first.
653 *
654 * @param out If provided, the @c status objects corresponding to
655 * completed requests will be emitted through this output iterator.
656
657 * @returns If the @p out parameter was provided, a pair containing
658 * the output iterator @p out after all of the @c status objects have
659 * been written through it and an iterator referencing the first
660 * completed request. If no @p out parameter was provided, only the
661 * iterator referencing the first completed request will be emitted.
662 */
663 template<typename BidirectionalIterator, typename OutputIterator>
664 std::pair<OutputIterator, BidirectionalIterator>
665 test_some(BidirectionalIterator first, BidirectionalIterator last,
666 OutputIterator out)
667 {
668 BidirectionalIterator current = first;
669 BidirectionalIterator start_of_completed = last;
670 while (current != start_of_completed) {
671 // Check if we have found a completed request.
672 if (optional<status> result = current->test()) {
673 using std::iter_swap;
674
675 // Emit the resulting status object
676 *out++ = *result;
677
678 // We're expanding the set of completed requests
679 --start_of_completed;
680
681 // Swap the request we just completed with the last request that
682 // has not yet been tested.
683 iter_swap(current, start_of_completed);
684
685 continue;
686 }
687
688 // Move to the next request.
689 ++current;
690 }
691
692 // Finish up by fixing the order of the completed set to match the
693 // order in which we emitted status objects, then return.
694 std::reverse(start_of_completed, last);
695 return std::make_pair(out, start_of_completed);
696 }
697
698 /**
699 * \overload
700 */
701 template<typename BidirectionalIterator>
702 BidirectionalIterator
703 test_some(BidirectionalIterator first, BidirectionalIterator last)
704 {
705 BidirectionalIterator current = first;
706 BidirectionalIterator start_of_completed = last;
707 while (current != start_of_completed) {
708 // Check if we have found a completed request.
709 if (optional<status> result = current->test()) {
710 using std::iter_swap;
711
712 // We're expanding the set of completed requests
713 --start_of_completed;
714
715 // Swap the request we just completed with the last request that
716 // has not yet been tested.
717 iter_swap(current, start_of_completed);
718
719 continue;
720 }
721
722 // Move to the next request.
723 ++current;
724 }
725
726 return start_of_completed;
727 }
728
729 } } // end namespace boost::mpi
730
731
732 #endif // BOOST_MPI_NONBLOCKING_HPP