The Boost C++ Libraries

Asynchronous data exchange

In addition to the blocking functions send() and recv(), Boost.MPI also supports asynchronous data exchange with the member functions isend() and irecv(). The names start with an i to indicate that the functions return immediately.

Example 47.7. Receiving data asynchronously with irecv()
#include <boost/mpi.hpp>
#include <boost/serialization/string.hpp>
#include <string>
#include <iostream>

int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  if (world.rank() == 0)
  {
    std::string s;
    boost::mpi::request r = world.irecv(boost::mpi::any_source, 16, s);
    if (r.test())
      std::cout << s << '\n';
    else
      r.cancel();
  }
  else
  {
    std::string s = "Hello, world!";
    world.send(0, 16, s);
  }
}

Example 47.7 uses the blocking function send() to send the string Hello, world! However, data is received with the asynchronous function irecv(). This member function expects the same parameters as recv(). The difference is that there is no guarantee that data has been received in s when irecv() returns.

irecv() returns an object of type boost::mpi::request. You can call test() to check whether data has been received. This member function returns a bool. You can call test() as often as you like. Because irecv() is an asynchronous member function, it is possible that the first call will return false and the second true. This would mean that the asynchronous operation was completed between the two calls.

Example 47.7 calls test() only once. If data has been received in s, the variable is written to the standard output stream. If no data has been received, the asynchronous operation is canceled with cancel().

If you run Example 47.7 multiple times, sometimes Hello, world! is displayed and sometimes there is no output. The outcome depends on whether data is received before test() is called.

Example 47.8. Waiting for multiple asynchronous operations with wait_all()
#include <boost/mpi.hpp>
#include <boost/serialization/string.hpp>
#include <string>
#include <iostream>

int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  if (world.rank() == 0)
  {
    boost::mpi::request requests[2];
    std::string s[2];
    requests[0] = world.irecv(1, 16, s[0]);
    requests[1] = world.irecv(2, 16, s[1]);
    boost::mpi::wait_all(requests, requests + 2);
    std::cout << s[0] << "; " << s[1] << '\n';
  }
  else if (world.rank() == 1)
  {
    std::string s = "Hello, world!";
    world.send(0, 16, s);
  }
  else if (world.rank() == 2)
  {
    std::string s = "Hello, moon!";
    world.send(0, 16, s);
  }
}

You can call test() on boost::mpi::request multiple times to detect when an asynchronous operation is complete. However, you can also call the blocking function boost::mpi::wait_all() as in Example 47.8. boost::mpi::wait_all() is a blocking function, but the advantage is that it can wait for multiple asynchronous operations to complete. boost::mpi::wait_all() returns when all asynchronous operations it is waiting for have been completed.

In Example 47.8, the process with rank 1 sends Hello, world! and the process with rank 2 Hello, moon! Since the order in which data is received doesn’t matter, the process with rank 0 calls irecv(). Since the program will only generate output when all asynchronous operations have been completed and all data has been received, the return values of type boost::mpi::request are passed to boost::mpi::wait_all(). Because boost::mpi::wait_all() expects two iterators, the objects of type boost::mpi::request are stored in an array. The begin and end iterators are passed to boost::mpi::wait_all().

Boost.MPI provides additional functions you can use to wait for the completion of asynchronous operations. boost::mpi::wait_any() returns when exactly one asynchronous operation is complete, and boost::mpi::wait_some() returns when at least one asynchronous operation has been completed. Both functions return a std::pair that indicates which operation or operations are complete.

boost::mpi::test_all(), boost::mpi::test_any(), and boost::mpi::test_some() test the status of multiple asynchronous operations with a single call. These functions are non-blocking and return immediately.