annotate 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
rev   line source
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