annotate DEPENDENCIES/generic/include/boost/mpi/nonblocking.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents c530137014c0
children
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@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