Sending and receiving

The communicator class of TPO++ provides a couple of methods for sending and receiving data. The most simplest way to communicate is using standard mode blocking send and receives calls. The following simple example shows, how to communicate a C++ basic type in TPO++:

#include <tpo++.H>
int main(int argc, char *argv[]) {
    TPO::init(&argc, &argv);

    if (TPO::CommWorld.rank()==0) {
      double data=3.14;
      TPO::CommWorld.send(data, 1);
    } else {
      double data=0.0;
      TPO::CommWorld.recv(data);
    }
}

On this example, the sender only provides the data to be sent and the rank of the target host. An optional message tag is omitted and defaults to $0$. The full syntax of the send method is:

class TPO::Communicator {
  template <class T>
  void send(const T& buf, const Rank& dest,  const Tag& tag = 0);
};
It works on arbitrary types, provided that TPO++ knows how to send them. Sending of user-defined types is more elaborated in section 4.4.

The receive method receives by default from any sender and messages with any tag, so the only argument provided is the data to be received. Further, all receive function return a object of type TPO::Status containg additional information about the received message, like the actual sender and tag. In the example the return status is ignored. Section 4.1 provides details on the status class. The full syntax of the receive method is:

class TPO::Communicator {
  template <class T> 
  Status recv(T& buf,
              const Rank& source = Rank::ANY_SOURCE,
              const Tag& tag = Tag::ANY_TAG);
};

The next example shows, how to send and receive Standard Template Library containers with TPO++:

#include <tpo++.H>
int main(int argc, char *argv[]) {
    TPO::init(&argc, &argv);

    if (TPO::CommWorld.rank()==0) {
      vector<double> VD(10);
      fill(VD.begin(), VD.end(), 3.14);
      TPO::CommWorld.send(VD.begin(), VD.end(), 1);
    } else {
      vector<double> VD(10);
      TPO::CommWorld.recv(VD.begin(), VD.end());
    }
}

In this example, we send a vector of double from host $0$ to host $1$. For transmitting a STL container, both, sender and receiver have to provide appropriate iterator pointing to the data to be sent or received. On the sender side, this allows to send subranges of containers using the same send call. On the receiver side, the application must provide enough space to store the received data, and specifies the a target range. This again allows to receive at an arbitrary offset in the container. The requirement to provide space is consistent with MPI semantics. If the receiver runs out of space, the result of the receive operation is undefined, consistent with STL semantics (f.ex. copy).

Currently TPO++ allows to send and receive STL lists, vectors, deques and stacks (?).

A - possible expensive - way to receive data without knowing its size in advance is the use of inserters. Inserters are special iterators allocating the space needed for insertion on demand. TPO++ provides a special net_back_inserter, which allows to receive into an empty container and allocates the space needed. The following example shows this approach:

#include <tpo++.H>
int main(int argc, char *argv[]) {
    TPO::init(&argc, &argv);

    if (TPO::CommWorld.rank()==0) {
      vector<double> VD(10);
      fill(VD.begin(), VD.end(), 3.14);
      TPO::CommWorld.send(VD.begin(), VD.end(), 1);
    } else {
      vector<double> VD;
      TPO::net_back_insert_iterator<vector<double> > result(VD);
      TPO::CommWorld.recv(result);
    }
}



Subsections
Patrick Heckeler 2007-05-31