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