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