PageRenderTime 75ms CodeModel.GetById 36ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/Src/Dependencies/Boost/boost/mpi/nonblocking.hpp

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