1 // Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>.
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)
7 /** @file nonblocking.hpp
9 * This header defines operations for completing non-blocking
10 * communication requests.
12 #ifndef BOOST_MPI_NONBLOCKING_HPP
13 #define BOOST_MPI_NONBLOCKING_HPP
15 #include <boost/mpi/config.hpp>
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>
26 namespace boost
{ namespace mpi
{
29 * @brief Wait until any non-blocking request has completed.
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
36 * @param first The iterator that denotes the beginning of the
37 * sequence of request objects.
39 * @param last The iterator that denotes the end of the sequence of
40 * request objects. This may not be equal to @c first.
42 * @returns A pair containing the status object that corresponds to
43 * the completed operation and the iterator referencing the completed
46 template<typename ForwardIterator
>
47 std::pair
<status
, ForwardIterator
>
48 wait_any(ForwardIterator first
, ForwardIterator last
)
52 BOOST_ASSERT(first
!= last
);
54 typedef typename
std::iterator_traits
<ForwardIterator
>::difference_type
57 bool all_trivial_requests
= true;
58 difference_type n
= 0;
59 ForwardIterator current
= first
;
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
);
65 // Check if this request (and all others before it) are "trivial"
66 // requests, e.g., they can be represented with a single
68 all_trivial_requests
=
70 && !current
->m_handler
71 && current
->m_requests
[1] == MPI_REQUEST_NULL
;
73 // Move to the next request.
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
;
82 for (current
= first
; current
!= last
; ++current
)
83 requests
.push_back(current
->m_requests
[0]);
85 // Let MPI wait until one of these operations completes.
88 BOOST_MPI_CHECK_RESULT(MPI_Waitany
,
89 (n
, &requests
[0], &index
, &stat
.m_status
));
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
));
96 // Find the iterator corresponding to the completed request.
98 advance(current
, index
);
99 current
->m_requests
[0] = requests
[index
];
100 return std::make_pair(stat
, current
);
103 // There are some nontrivial requests, so we must continue our
104 // busy waiting loop.
107 all_trivial_requests
= true;
111 // We cannot ever get here
116 * @brief Test whether any non-blocking request has completed.
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.
124 * @param first The iterator that denotes the beginning of the
125 * sequence of request objects.
127 * @param last The iterator that denotes the end of the sequence of
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<>.
135 template<typename ForwardIterator
>
136 optional
<std::pair
<status
, ForwardIterator
> >
137 test_any(ForwardIterator first
, ForwardIterator last
)
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
);
146 return optional
<std::pair
<status
, ForwardIterator
> >();
150 * @brief Wait until all non-blocking requests have completed.
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
157 * @param first The iterator that denotes the beginning of the
158 * sequence of request objects.
160 * @param last The iterator that denotes the end of the sequence of
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
168 * @returns If an @p out parameter was provided, the value @c out
169 * after all of the @c status objects have been emitted.
171 template<typename ForwardIterator
, typename OutputIterator
>
173 wait_all(ForwardIterator first
, ForwardIterator last
, OutputIterator out
)
175 typedef typename
std::iterator_traits
<ForwardIterator
>::difference_type
180 difference_type num_outstanding_requests
= distance(first
, last
);
182 std::vector
<status
> results(num_outstanding_requests
);
183 std::vector
<bool> completed(num_outstanding_requests
);
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;
197 // Check if this request (and all others before it) are "trivial"
198 // requests, e.g., they can be represented with a single
200 all_trivial_requests
=
202 && !current
->m_handler
203 && current
->m_requests
[1] == MPI_REQUEST_NULL
;
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]);
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],
224 for (std::vector
<MPI_Status
>::iterator i
= stats
.begin();
225 i
!= stats
.end(); ++i
, ++out
) {
234 all_trivial_requests
= false;
237 return std::copy(results
.begin(), results
.end(), out
);
243 template<typename ForwardIterator
>
245 wait_all(ForwardIterator first
, ForwardIterator last
)
247 typedef typename
std::iterator_traits
<ForwardIterator
>::difference_type
252 difference_type num_outstanding_requests
= distance(first
, last
);
254 std::vector
<bool> completed(num_outstanding_requests
);
256 while (num_outstanding_requests
> 0) {
257 bool all_trivial_requests
= true;
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;
268 // Check if this request (and all others before it) are "trivial"
269 // requests, e.g., they can be represented with a single
271 all_trivial_requests
=
273 && !current
->m_handler
274 && current
->m_requests
[1] == MPI_REQUEST_NULL
;
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]);
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
));
295 num_outstanding_requests
= 0;
301 * @brief Tests whether all non-blocking requests have completed.
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.
314 * @param first The iterator that denotes the beginning of the
315 * sequence of request objects.
317 * @param last The iterator that denotes the end of the sequence of
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).
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.
331 template<typename ForwardIterator
, typename OutputIterator
>
332 optional
<OutputIterator
>
333 test_all(ForwardIterator first
, ForwardIterator last
, OutputIterator out
)
335 std::vector
<MPI_Request
> requests
;
336 for (; first
!= last
; ++first
) {
337 // If we have a non-trivial request, then no requests can be
339 if (first
->m_handler
|| first
->m_requests
[1] != MPI_REQUEST_NULL
)
340 return optional
<OutputIterator
>();
342 requests
.push_back(first
->m_requests
[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]));
350 for (int i
= 0; i
< n
; ++i
, ++out
) {
352 stat
.m_status
= stats
[i
];
357 return optional
<OutputIterator
>();
364 template<typename ForwardIterator
>
366 test_all(ForwardIterator first
, ForwardIterator last
)
368 std::vector
<MPI_Request
> requests
;
369 for (; first
!= last
; ++first
) {
370 // If we have a non-trivial request, then no requests can be
372 if (first
->m_handler
|| first
->m_requests
[1] != MPI_REQUEST_NULL
)
375 requests
.push_back(first
->m_requests
[0]);
379 int n
= requests
.size();
380 BOOST_MPI_CHECK_RESULT(MPI_Testall
,
381 (n
, &requests
[0], &flag
, MPI_STATUSES_IGNORE
));
386 * @brief Wait until some non-blocking requests have completed.
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.
396 * @param first The iterator that denotes the beginning of the
397 * sequence of request objects.
399 * @param last The iterator that denotes the end of the sequence of
400 * request objects. This may not be equal to @c first.
402 * @param out If provided, the @c status objects corresponding to
403 * completed requests will be emitted through this output iterator.
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.
411 template<typename BidirectionalIterator
, typename OutputIterator
>
412 std::pair
<OutputIterator
, BidirectionalIterator
>
413 wait_some(BidirectionalIterator first
, BidirectionalIterator last
,
419 return std::make_pair(out
, first
);
421 typedef typename
std::iterator_traits
<BidirectionalIterator
>::difference_type
424 bool all_trivial_requests
= true;
425 difference_type n
= 0;
426 BidirectionalIterator current
= first
;
427 BidirectionalIterator start_of_completed
= last
;
429 // Check if we have found a completed request.
430 if (optional
<status
> result
= current
->test()) {
431 using std::iter_swap
;
433 // Emit the resulting status object
436 // We're expanding the set of completed requests
437 --start_of_completed
;
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,
444 std::reverse(start_of_completed
, last
);
445 return std::make_pair(out
, start_of_completed
);
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
);
455 // Check if this request (and all others before it) are "trivial"
456 // requests, e.g., they can be represented with a single
458 all_trivial_requests
=
460 && !current
->m_handler
461 && current
->m_requests
[1] == MPI_REQUEST_NULL
;
463 // Move to the next request.
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
);
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
);
482 for (current
= first
; current
!= last
; ++current
)
483 requests
.push_back(current
->m_requests
[0]);
485 // Let MPI wait until some of these operations complete.
487 BOOST_MPI_CHECK_RESULT(MPI_Waitsome
,
488 (n
, &requests
[0], &num_completed
, &indices
[0],
491 // Translate the index-based result of MPI_Waitsome into a
492 // partitioning on the requests.
493 int current_offset
= 0;
495 for (int index
= 0; index
< num_completed
; ++index
, ++out
) {
496 using std::iter_swap
;
498 // Move "current" to the request object at this index
499 advance(current
, indices
[index
] - current_offset
);
500 current_offset
= indices
[index
];
502 // Emit the status object
504 stat
.m_status
= stats
[index
];
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
);
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
);
521 // There are some nontrivial requests, so we must continue our
522 // busy waiting loop.
528 // We cannot ever get here
535 template<typename BidirectionalIterator
>
536 BidirectionalIterator
537 wait_some(BidirectionalIterator first
, BidirectionalIterator last
)
544 typedef typename
std::iterator_traits
<BidirectionalIterator
>::difference_type
547 bool all_trivial_requests
= true;
548 difference_type n
= 0;
549 BidirectionalIterator current
= first
;
550 BidirectionalIterator start_of_completed
= last
;
552 // Check if we have found a completed request.
553 if (optional
<status
> result
= current
->test()) {
554 using std::iter_swap
;
556 // We're expanding the set of completed requests
557 --start_of_completed
;
559 // If we have hit the end of the list of pending requests, we're
561 if (current
== start_of_completed
)
562 return start_of_completed
;
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
);
571 // Check if this request (and all others before it) are "trivial"
572 // requests, e.g., they can be represented with a single
574 all_trivial_requests
=
576 && !current
->m_handler
577 && current
->m_requests
[1] == MPI_REQUEST_NULL
;
579 // Move to the next request.
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
;
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
);
593 for (current
= first
; current
!= last
; ++current
)
594 requests
.push_back(current
->m_requests
[0]);
596 // Let MPI wait until some of these operations complete.
598 BOOST_MPI_CHECK_RESULT(MPI_Waitsome
,
599 (n
, &requests
[0], &num_completed
, &indices
[0],
600 MPI_STATUSES_IGNORE
));
602 // Translate the index-based result of MPI_Waitsome into a
603 // partitioning on the requests.
604 int current_offset
= 0;
606 for (int index
= 0; index
< num_completed
; ++index
) {
607 using std::iter_swap
;
609 // Move "current" to the request object at this index
610 advance(current
, indices
[index
] - current_offset
);
611 current_offset
= indices
[index
];
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
);
620 // We have satisfied some requests, so we are done.
621 return start_of_completed
;
624 // There are some nontrivial requests, so we must continue our
625 // busy waiting loop.
631 // We cannot ever get here
636 * @brief Test whether some non-blocking requests have completed.
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
648 * @param first The iterator that denotes the beginning of the
649 * sequence of request objects.
651 * @param last The iterator that denotes the end of the sequence of
652 * request objects. This may not be equal to @c first.
654 * @param out If provided, the @c status objects corresponding to
655 * completed requests will be emitted through this output iterator.
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.
663 template<typename BidirectionalIterator
, typename OutputIterator
>
664 std::pair
<OutputIterator
, BidirectionalIterator
>
665 test_some(BidirectionalIterator first
, BidirectionalIterator last
,
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
;
675 // Emit the resulting status object
678 // We're expanding the set of completed requests
679 --start_of_completed
;
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
);
688 // Move to the next request.
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
);
701 template<typename BidirectionalIterator
>
702 BidirectionalIterator
703 test_some(BidirectionalIterator first
, BidirectionalIterator last
)
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
;
712 // We're expanding the set of completed requests
713 --start_of_completed
;
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
);
722 // Move to the next request.
726 return start_of_completed
;
729 } } // end namespace boost::mpi
732 #endif // BOOST_MPI_NONBLOCKING_HPP