Basic Communication¶
The following classes are all defined within namespace HIPP::MPI.
Class Comm: Communicator¶
-
class
Comm: public MPIObj<_Comm>¶ Comm is the high-level communicator interface for MPI system, which encapsulates the group of processes involved in the communication, the topology of them, and also defines an isolated communication context. All point-to-point and collective communications within this communicator are started by calling the method of
Comminstance.The instance of
Commshould not be directly constructed. It should be returned by other function calls like the communicator management functions inComm, or by theEnv::world()method. After you have a initial communicator like the the communication world, it is easy to operate on its process group and useComm::create()to create a new communicator.The
Commobject can be copy-constructed, copy-assigned, move-constructed and move-assigned. The copy operation gives a object that refers to the same commnicator (internally the sameMPI_Comm). The destructor isnoexcept.-
typedef std::function<bool(Comm &oldcomm, int keyval, void *extra_state, void *attr_val, void *&attr_val_out)>
copy_attr_fn_t¶ -
typedef std::function<void(Comm &comm, int keyval, void *attr_val, void *extra_state)>
del_attr_fn_t¶ The attribute caching copy function and delete function types. See MPI Standard for the definition of attribute caching. HIPP allows you to provide any function or function-like object (including lambda) as copy and delete functions.
-
static const copy_attr_fn_t
NULL_COPY_FN¶ -
static const copy_attr_fn_t
DUP_FN¶ -
static const del_attr_fn_t
NULL_DEL_FN¶ Predefined copy and delete functions for attribute caching.
NULL_COPY_FNdoes not copy attribute, andDUP_FNjust copy the attribute value.NULL_DEL_FNdoes nothing at the delete of the attribute.
-
void
free() noexcept¶ free the current communicator instance, and set it to a null communicator as returned by
Comm::nullval().freecan be called at any time and repeatly, even for the pre-defined communicators and null communicators.
-
ostream &
info(ostream &os = cout, int fmt_cntl = 1) const¶ -
friend ostream &
operator<<(ostream &os, const Comm &comm)¶ info()displays some basic information of the communicator instance toos.- Parameters
fmt_cntl – Control the display format. 0 for inline information and 1 for a verbose, multiple-line information. 2 for a exhausted priting, with lots of information to be printed.
- Returns
The argument
osis returned.
The overloaded << operator is equivalent to
info()with defaultfmt_cntl.The returned reference of
osallows you to chain the outputs, such ascomm.info(cout) << " continue printing " << std::endl.
-
int
size() const¶ -
int
rank() const¶ -
bool
is_null() const¶ -
bool
is_inter() const¶ -
int
remote_size() const¶ Return the basic information of the communicater instance:
size()returns the number of processes in the group that forms this communicater.rank()returns the identifier of the caller process, which is in the range of [0, size()).is_null()tests whether this is a NULL communicater (internally MPI_PROC_NULL).is_inter()tests whether this is an inter-communicater, if it is,remote_size()returns the number of processes in the remote group of the inter-communicater.
-
static int
create_keyval(copy_attr_fn_t copy_attr_fn = NULL_COPY_FN, del_attr_fn_t del_attr_fn = NULL_DEL_FN, void *extra_state = nullptr)¶ -
static void
free_keyval(int &keyval)¶ -
Comm &
set_attr(int keyval, void *attr_val)¶ -
bool
get_attr(int keyval, void *&attr_val) const¶ -
Comm &
del_attr(int keyval)¶ -
template<typename
AttrT>
static intcreate_keyval()¶ -
template<typename
AttrT>
boolget_attr(int keyval, AttrT *&attr_val) const¶ Attribute caching calls (see MPI Standard for a detailed description).
create_keyval()creates a key value for attribute caching, with args specifying its copy and delete functions, and passing extra state. The created key value is returned.free_keyval()frees a key values specified by the argkeyval(user should ensure that the key is not used, possibly by callingdel_attr()on call communicators that use this key).If the attribute is set, the copy function is invoked on
Comm::dup(), and the delete function is invoked on the destruction (when all instances refering to the internal communicator object are destroyed) orComm::set_attr().set_attr()sets the attribute corresponding to thekeyval. The attribute is a typevoid *variable, typically cast from an integer or pointing to an address of a heap object.get_attr()gets the attribute value corresponding tokeyvalinto argattr_valand returns true. If the attribute is not set yet, returns false.del_attr()removes the attribute, which invoke the delete function.In the templated version, the cached attribute set by user must be a pointer to
AttrTwhich is dynamically allocated with new operator. The templatedcreate_keyval()uses the copy constructor and destructor ofAttrTas the copy function and delete function to make new heap object and delete existing heap object, and setextra_state = nullptr.Possible usage example:
struct AttrT{ int a = 1; double b = 2.; }; keyval = HIPP::MPI::Comm::create_keyval<AttrT>(); // Create a key for attribute caching. comm.set_attr(keyval, new AttrT); // Set the attribute to communicator 'comm'. auto new_comm = comm.dup(); // At 'dup', new_comm get a copy of attribute. AttrT *attr_val; if( new_comm.get_attr(keyval, attr_val) ) // Now we print it. cout << "a=" << attr_val->a << ", b=" << attr_val->b << endl; comm.del_attr(keyval); // Delete all attributes, and free the key. new_comm.del_attr(keyval); HIPP::MPI::Comm::free_keyval(keyval);
Output (from a single process) is:
a=1, b=2
-
Comm
split(int color, int key = 0) const¶ -
Comm
dup() const¶ -
Comm
create(const Group &group) const¶ -
static Comm
world() noexcept¶ -
static Comm
selfval() noexcept¶ -
static Comm
nullval() noexcept¶ -
Comm
create_inter(int local_leader, const Comm &peer_comm, int remote_leader, int tag)¶ -
Comm
merge_inter(int high)¶ Communicator management functions - get new communicaters or return existing communicaters.
Because communicater provides the context and topology of the communication, it is always a good idea to create new communicators, which helps encapsulate your library development and simplify the communication logic.
split()splits the current group of processes into several disjoints ones, and returns communicators that host these new groups. This is a collective operation of the processes in the old group, and the returned communicater is that the caller process resides in.- Parameters
color – processes with the same color is grouped into the same new group. If a process does not want a new communicater, set
color=UNDEFINED, in such a case the split operation returns a null process as returned byComm::nullval().key – specify the rank of processes in the new group. Process has a smaller key will have a smaller rank in the new group. Processes with the same key will ordered according to their ranks in the old group.
dup()copies the current communicator and retunrs a new one. This is a collective operation of the old communicater. Note that the attribute cahched will also be copied according to the copy function specified in the creation of the key value (see API/MPI/Comm Attribute Caching Calls).create()create new communicators according to the group arguments. Processes that you want to put in the same new communicater should call with a group argument containing them, with same rank order. Pass an empty group as returned byGroup::emptyvall()if a process does not need a new communicater. On return, the process that belongs to its group argument get a new communicater, if it does not belongs to its group argument (e.g., an empty group), return a null communicater as returned byComm::nullval().world(),selfval()andnullval()return the predefined communicators - the world communicater, the communicater that contains only self, and the null communicater, respectively. These calls are local.create_inter()creates and returns a new inter-communicater. This call is collective over the union of the local and remote groups. This must be called by two groups of processes (two intra-communicaters), and within in each of the group, processes provide the same rank of the local leader. The local leader must specify a peer communicater that contains at least self and the remote leader (for non-leader process,remote_leaderis not significant), and atagthat is used for point-to-point communication on the creation of the inter-communicator.merge_inter()merges the groups in a inter-communicator, and returns a intra communicater.Example of creating an inter-communicator, and using it to perform the collective communication:
// Creation of an inter-communicator that has one master and multiple workers. int rank = comm.rank(), local_leader = 0, remote_leader = (rank==0)?1:0, tag = 0; auto inter_comm = comm.split(rank==0).create_inter(local_leader, comm, remote_leader, tag); // Perform inter-collective-communication with the communicator. auto &dtype = HIPP::MPI::INT; if( rank == 0 ){ int out_buff = rank, count = 1, root=HIPP::MPI::ROOT; vector<int> in_buff(inter_comm.remote_size()); inter_comm.bcast(&out_buff, count, dtype, root); inter_comm.gather(NULL, 0, dtype, in_buff.data(), count, dtype, root); // To avoid output entanglement, use SeqBlock to serialize the following statements. HIPP::MPI::SeqBlock seq(comm); cout << "Master " << "sends " << out_buff << " and recvs "; HIPP::prt_a(cout, in_buff) << endl; }else{ int out_buff = rank, count = 1, in_buff, root = 0; inter_comm.bcast(&in_buff, count, dtype, root); inter_comm.gather(&out_buff, count, dtype, NULL, 0, dtype, root); HIPP::MPI::SeqBlock seq(comm); cout << "Worker " << inter_comm.rank() << " recvs " << in_buff << " and sends " << out_buff << endl; }
Here we create a master-slave model. The communication in such a model is perfectly described by an inter-communicator. We let rank-0 process in
commto become master, and the remaining processes are slaves/workers. Then, the master broadcast a message to all workers, and workers reply the master by a gather. Note that we use the synchronization extensionMPI::SeqBlockto avoid entanglement of the output (i.e., outputs will be serialized across processes).The output is (run with 5 processes in total)
Master sends 0 and recvs 1,2,3,4 Worker 0 recvs 0 and sends 1 Worker 1 recvs 0 and sends 2 Worker 2 recvs 0 and sends 3 Worker 3 recvs 0 and sends 4
-
const Group
group() const¶ -
Group
group()¶ -
const Group
remote_group() const¶ -
Group
remote_group()¶ group()returns the (local) group of processes in the communicator. If this is an inter-communicator,remote_group()returns the remote group of procecess.
-
Comm
cart_create(const vector<int> &dims, const vector<int> &periods, int reorder = 1) const¶ -
static void
dims_create(int nnodes, int ndims, vector<int> &dims)¶ -
int
cartdim_get() const¶ -
void
cart_get(vector<int> &dims, vector<int> &periods, vector<int> &coords) const¶ -
int
cart_rank(const vector<int> &coords) const¶ -
vector<int>
cart_coords(int rank) const¶ -
void
cart_shift(int direction, int disp, int &rank_src, int &rank_dest) const¶ -
Comm
cart_sub(const vector<int> &remain_dims)¶ -
int
topo_test() const¶ Virtual topology management.
cart_create()create a new communicator with number of processes at each dimension specified bydims, whether periodic at each dimension specified byperiods. Ifreorderis not zero then implementation is allowed to reorder the ranks of the processes and then put then on cartesian grids (otherwise processes are put by row-major order according to their ranks). If size of the origin communicator is larger than needed, processes that is not put on the grids get null communicater as returned byComm::nullval().dims_create()is a helpful function to determine the number of processes at each dimension from the number of process available in total,nnodes, and disired number of dimensions,ndims.dimsserves are both input and output arg, on entry, positivedims[i]will not changed on exit, zerodims[i]will be changed to a suitable value. Changed dims will be in an non-increasing order, and they are as close as possible. If on entry, nnodes is not multiple of prod(dims[i]) (for all dims[i] != 0), an error will occur.It is valid to pass a dims with length not equal to
ndims.dimsis resized tondims(padding with 0 if necessary).Example: a call of
Comm::cart_create(nnodes, ndims, dims)gives results asnnodes and ndims
input and output dims
6, 2
(0,0) -> (3,2)
7, 2
(0,0) -> (7,1)
6, 3
(0,3,0) -> (2,3,1)
7, 3
(0,3,0) -> erroneous call
For an communicator with cartesian topology, the following calls inquiry its information.
cartdim_get()returns the number of dimensions.cart_get()returns number of processes in each dimension, whether each dimension is periodic, and the coordinates of the calling process, into argsdims,periodsandcoords, respectively.cart_rank()accepts coordinatescoordsin the topology and return itsrankin the communicator. For periodic dimension,coords[i]is shifted to valid range, otherwise an out-of-rangecoords[i]is erroneous. For zero-dimensional topology,coordsis not significant and the call retunrs 0.cart_coords()convert therankin the communicator into the coordinates.cart_shift()find the neighbor ranks of the calling process at dimension specified bydirectionand displacement (positive) specified bydisp. Return the ranks of the processes offset by-dispanddispat this dimension intorank_src,rank_dest, respectively.cart_sub()decompose the original cartesian topology into several sub-cartesian communicators. The remaining dimensions are passed asremain_dims, and decomposition happens at the non-remaining direction.topo_test()return the topology type of the communicator. Possible values areUNDEFINED,GRAPH,CART,DIST_GRAPHin theHIPP::MPInamespace.
-
Win
win_create(void *base, aint_t size, int disp_unit, const Info &info = Info::nullval()) const¶ -
Win
win_create_dynamic(const Info &info = Info::nullval()) const¶ -
Win
win_allocate(void *&base_ptr, aint_t size, int disp_unit, const Info &info = Info::nullval()) const¶ -
template<typename
T>
std::pair<Win, T*>win_allocate(size_t n, int disp_unit = sizeof(T), const Info &info = Info::nullval()) const¶ Remote memory access (RMA) window creation functions.
Those functions create RMA window by different ‘flavors’.
win_create()create a RMA window by attaching a memory buffer starting atbasewithsizebytes.win_allocate()does similar thing, but instead of attach user’s buffer, it allocate a memory buffer and return its address bybase_ptr.win_allocate_shared()is similar towin_allocate()but require the allocated memory can be directly load/store by other processes (which is only possible for processes in a shared-memory system).The templated version of
win_allocate()andwin_allocate_shared()are usually more convient than the non-templated version. They allocate memory forntypeTvariables (i.e., sizeof(T)*n bytes).The common args of these functions are:
- Parameters
info – info object to ‘hint’ the implementation. A null info (as returned by
Info::nullval()is always valid). See MPI Standard for which hints are defined. See also the implementation manual for implementation-specific hints.disp_unit – specify the displacement unit in the RMA operation started by any ‘remote’ process. It suggested to set ‘disp_unit’ to the size of the data type if memory buffer is occupied by elements of a single type, or set to 1 otherwise.
Example of creation and usage of the RMA window object:
constexpr int N = 5; vector<double> out_buff(N, comm.rank()); auto [win, in_buff] = comm.win_allocate<double>(N); int disp = 0; { int dest = (comm.rank()+1) % comm.size(); auto guard = win.fence_g(); // RMA synchronization call. win.put(dest, out_buff, disp); // RMA PUT call. } { // Now, sequentially print the local data and those received from // other process. HIPP::MPI::SeqBlock seq(comm); cout << "Rank: " << comm.rank() << " has put out "; HIPP::prt_a(cout, out_buff) << " and received "; HIPP::prt_a(cout, in_buff, in_buff+N) << endl; }
In the above, we create a RMA window and use it to put the local data in
in_buffto a remote window of the ‘next’ process in the communicator. With proper fence synchronization, data will be seen by the remote process after exit of the first block. Each process is then print the data in its local buffer and those received from other process. Note that we use the synchronization extensionMPI::SeqBlockto avoid entanglement of the output (i.e., outputs will be serialized across processes).The output is (run with 3 processes)
Rank: 0 has put out 0,0,0,0,0 and received 2,2,2,2,2 Rank: 1 has put out 1,1,1,1,1 and received 0,0,0,0,0 Rank: 2 has put out 2,2,2,2,2 and received 1,1,1,1,1
-
template<typename ...
Args>
voidsend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
voidbsend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
voidssend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
voidrsend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
Statusrecv(int src, int tag, Args&&... args) const¶ -
template<typename ...
Args>
Requestsisend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
Requestsibsend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
Requestsissend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
Requestsirsend(int dest, int tag, Args&&... args) const¶ -
template<typename ...
Args>
Requestsirecv(int src, int tag, Args&&... args) const¶ Point-to-point communication functions - send messages and receive messages. These are traditional calls lying in the heart of MPI.
The MPI Standard defines both blocking and non-blocking versions (the later is started with an ‘i’). For each version, four send modes are provided: the standard mode (
send()/isend()), the buffered mode (bsend()/ibsend()), the synchronous mode (ssend()/issend()) and the ready mode (rsend()/irsend()). If you are not sure about the semantics of the communication modes, choose the standard mode. Otherwise it is suggested to carefully read the Standard specifications before using non-standard modes.The blocking
recv()returns aStatusobject containing the information that has been received. The non-blocking isends andirecv()returnRequestsobject for handling the completion/testing of the communication.Common arguments of these calls are:
- Parameters
dest – rank of the target process of the communication, i.e., source of a recv call and target of a send call. Recv calls can use the wildcard
ANY_SOURCEto match message from any process. Both recv and send calls can specifyPROC_NULLas target rank, then the call has no effect and returns immediately.tag – a tag for matching the send/recv operation pairs. Wildcard
ANY_TAGare allowed for recv calls to match any tags.args – specify the data buffer to be sent/received. Four cases are valid, see below.
The valid
argsare:(const) void *buff, int size, Datatype dtype: the most Standard way of specifying a buffer in MPI, the starting addressbuff, number of elementssizeof typedtype.(const) void *buff, int size, const std::string dtype: similar to the first, but use a string to specify the datatype. Only predefined datatypes are allowed, such as int, float, etc (seeDatatype).(const) vector<T, A> & vwhere T are any predefined types, such as int, float, etc (seeDatatype): send from/recv to the vector of elements, which is equivalent to the tripletv.data(), v.size(), "T".const std::string &s: send the string of characters. Only send call can use this signature, because a std::string is not writable.
The first three cases accept both const and non-const version. Due to the semantics of point-to-point communication, it is erroneous to pass a pure-right-value or x-value (i.e., a temporary variable) as
vors, because, user must ensure not using the buffer before the completion of communication.
-
Status
probe(int src, int tag) const¶ -
Status
iprobe(int src, int tag, int &flag) const¶ -
std::pair<Status, Message>
mprobe(int src, int tag) const¶ -
std::pair<Status, Message>
improbe(int src, int tag, int &flag) const¶ The probe operations allow incoming messages to be checked for, without actually receiving them. In all probe calls,
srcandtagspecify the target message to be checked for (which can be wildcards), in the calling communicators. The blocking versionprobe()andmprobe()wait until one message is found, while the non-blocking versioniprobe()andimprobe()return immediately, with theflagindicating whether the message is found.A
Statusobject is returned to allow the check of message details. A matched versionmprobe()orimprobe()also return aMessageobject to allow receiving calls precisely applied to the matched message, which may be helpful in a threaded program.
-
void
barrier() const¶ -
void
bcast(void *buf, int count, const Datatype &dtype, int root) const¶ -
void
gather(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype, int root) const¶ -
void
gatherv(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, const int recvcounts[], const int displs[], const Datatype &recvtype, int root) const¶ -
void
scatter(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype, int root) const¶ -
void
scatterv(const void *sendbuf, const int sendcounts[], const int displs[], const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype, int root) const¶ -
void
allgather(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype) const¶ -
void
allgatherv(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, const int recvcounts[], const int displs[], const Datatype &recvtype) const¶ -
void
alltoall(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype) const¶ -
void
alltoallv(const void *sendbuf, const int sendcounts[], const int senddispls[], const Datatype &sendtype, void *recvbuf, const int recvcounts[], const int recvdispls[], const Datatype &recvtype) const¶ -
void
alltoallw(const void *sendbuf, const int sendcounts[], const int senddispls[], const Datatype::mpi_t sendtypes[], void *recvbuf, const int recvcounts[], const int recvdispls[], const Datatype::mpi_t recvtypes[]) const¶ -
void
reduce(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op, int root) const¶ -
void
allreduce(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op) const¶ -
static void
reduce_local(const void *inbuf, void *inoutbuf, int count, const Datatype &dtype, const Oppacket &op)¶ -
void
reduce_scatter_block(const void *sendbuf, void *recvbuf, int recvcount, const Datatype &dtype, const Oppacket &op) const¶ -
void
reduce_scatter(const void *sendbuf, void *recvbuf, const int recvcounts[], const Datatype &dtype, const Oppacket &op) const¶ -
void
scan(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op) const¶ -
void
exscan(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op) const¶ -
Requests
ibarrier() const¶ -
Requests
ibcast(void *buf, int count, const Datatype &dtype, int root) const¶ -
Requests
igather(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype, int root) const¶ -
Requests
igatherv(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, const int recvcounts[], const int displs[], const Datatype &recvtype, int root) const¶ -
Requests
iscatter(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype, int root) const¶ -
Requests
iscatterv(const void *sendbuf, const int sendcounts[], const int displs[], const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype, int root) const¶ -
Requests
iallgather(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype) const¶ -
Requests
iallgatherv(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, const int recvcounts[], const int displs[], const Datatype &recvtype) const¶ -
Requests
ialltoall(const void *sendbuf, int sendcount, const Datatype &sendtype, void *recvbuf, int recvcount, const Datatype &recvtype) const¶ -
Requests
ialltoallv(const void *sendbuf, const int sendcounts[], const int senddispls[], const Datatype &sendtype, void *recvbuf, const int recvcounts[], const int recvdispls[], const Datatype &recvtype) const¶ -
Requests
ialltoallw(const void *sendbuf, const int sendcounts[], const int senddispls[], const Datatype::mpi_t sendtypes[], void *recvbuf, const int recvcounts[], const int recvdispls[], const Datatype::mpi_t recvtypes[]) const¶ -
Requests
ireduce(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op, int root) const¶ -
Requests
iallreduce(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op) const¶ -
Requests
ireduce_scatter_block(const void *sendbuf, void *recvbuf, int recvcount, const Datatype &dtype, const Oppacket &op) const¶ -
Requests
ireduce_scatter(const void *sendbuf, void *recvbuf, const int recvcounts[], const Datatype &dtype, const Oppacket &op) const¶ -
Requests
iscan(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op) const¶ -
Requests
iexscan(const void *sendbuf, void *recvbuf, int count, const Datatype &dtype, const Oppacket &op) const¶ Collective communication functions for data movement or group computation.
According to MPI standard, some collective communication functions can apply to both inter and intra communicators. If inter-communicators are used, then only the ‘all to all’ functions are bi-directional, others are uni-directional.
Some recv/send buffer can be specified with a
IN_PLACE, this is exactly the same as the Standard MPI_IN_PLACE.The non-blocking version here returns a
Requestsobject for later testing and completion. The requests object should not be freed manually before completion.In all cases, the datatype argument mush be exactly a
Datatypeinstance or an array of such. This is different from the point-to-point communication, where you can pass a string to indicate a basic type. One exception isalltoallw()andialltoallw(), in which the datatype arguments is an array of original MPI datatype as returned by methodDatatype::raw()(this design avoid the problem when using non-blocking collective operation, and also avoid overhead in converting the datatype from high-level instance to MPI original one).Please refer to the Standard for the detailed semantics of these collective calls.
Examples:
A typical point-to-point communication is displayed. The process with rank 0 send a vector of values to each of the other processes:
int rank = comm.rank(), size = comm.size(); HIPP::MPI::Mutex mtx(comm); // Initialize a mutex for exclusive printing. constexpr int count = 5, tag = 0; if( rank == 0 ){ // Process 0 sends a vector of values to each of the other processes. for(int i=1; i<size; ++i){ vector<double> out_buff(count, i); comm.send(i, tag, out_buff); } }else{ // Receive the vector of values from rank-0 process. Then print it. vector<double> in_buff(count); comm.recv(0, tag, in_buff.data(), count, HIPP::MPI::DOUBLE); mtx.lock(); // Avoid the entanglement of output. cout << "Rank " << rank << " receives "; HIPP::prt_a(cout, in_buff) << endl; mtx.unlock(); }
We display two ways of specifying the communication buffer by using either a single vector or a standard MPI triplet
(address, count, datatype). Four ways of specifying buffer are avaiable, see API/MPI/Point-to-point Communication. The output is (order may be different at runs)Rank 4 receives 4,4,4,4,4 Rank 2 receives 2,2,2,2,2 Rank 1 receives 1,1,1,1,1 Rank 3 receives 3,3,3,3,3
To show an alternative way of point-to-point communication, we use the non-standard mode (‘ready’ mode here). To use the ‘ready’ mode, the receive side prepares the receive buffer and start a non-blocking receive. It then notifies the sender to ask a response:
if( rank == 0 ){ /** * Process 0 waits for each of the other processes to send a notification. * Then it make a response. * The notification has no data, so buff = NULL, count = 0, * datatype = any ("char" here). * The reponse uses a 'ready' mode because the target must get ready. */ for(int i=1; i<size; ++i){ // auto status = comm.recv(HIPP::MPI::ANY_SOURCE, tag, NULL, 0, "char"); vector<double> out_buff(count, i); comm.rsend(status.source(), tag, out_buff); } }else{ /** * Prepare a buffer 'in_buff' and start the non-blocking recv. * Then, notify process 0 and wait for response. */ vector<double> in_buff(count); auto request = comm.irecv(0, tag, in_buff); comm.send(0, tag, NULL, 0, "char"); request.wait(); mtx.lock(); // Avoid the entanglement of output. cout << "Rank " << rank << " receives "; HIPP::prt_a(cout, in_buff) << endl; mtx.unlock(); }
The output is similar to the previous example using standard send/recv.
-
typedef std::function<bool(Comm &oldcomm, int keyval, void *extra_state, void *attr_val, void *&attr_val_out)>
Class Group: Process Collection¶
-
class
Group: public MPIObj<_Group>¶ Groupis the high-level interface for group of process.As in the Standard MPI, a group of processes defines the process name-ranking, which is the basis of point-to-point communication. a process group also defines the involved processes of a collective communication.
An initial group instance should be obtained from a communicator through method
Comm::group(). After that, you may apply the group transformation functions (union, intersection, difference, …) to create new groups based on existing groups.The life time of the group is manipulated by the instance, and you are not necessary to manually control it. However, you may call
free()to free the group instance in advance.The
Groupobject can be copy-constructed, copy-assigned, move-constructed and move-assigned. The copy operation gives a object that refers to the same process group (internally the sameMPI_Group). The destructor isnoexcept.-
void
free() noexcept¶ free the group instance and set it to a null value as returned by
Group::nullval().Calling
free()is not necessary for any group, since the life time is controlled automatically, but you may want to release the resources in advance.free()can be called at any time, and even multiple times, and even when the instance is a null value or a predefined value.
-
ostream &
info(ostream &os = cout, int fmt_cntl = 1) const¶ -
friend ostream &
operator<<(ostream &os, const Group &group)¶ info()prints the information of the current instance to the streamos.- Parameters
fmt_cntl – control the amount of information to be printed, 0 for a short and inline priting, 1 for a verbose, multi-line version.
- Returns
The argument
osis returned.
The overloaded << operator is equivalent to
info()with defaultfmt_cntl.
-
int
size() const¶ -
int
rank() const¶ -
int
is_null() const¶ -
vector<int>
translate_ranks(const vector<int> &ranks, const Group &othergroup) const¶ -
int
compare(const Group &othergroup) const¶ Inquery the information of the group instance.
size()gives the number of processes in this group.rank()returns the rank of the current process in this group. If the calling process is not in the group, return UNDEFINED.is_null()tests whether the group is a null value/null instance.translate_ranks()accepts the ranks of processes in the group instance, returns their ranks in another groupothergroup.compare()compares two groups. It may return IDENT, SIMILAR or UNEQUAL. See the Standard MPI specification for detail.
-
Group
union_(const Group &othergroup) const¶ -
Group
intersection(const Group &othergroup) const¶ -
Group
difference(const Group &othergroup) const¶ -
Group
incl(const vector<int> &ranks) const¶ -
Group
excl(const vector<int> &ranks) const¶ -
Group
range_incl(const vector<int> &ranks) const¶ -
Group
range_excl(const vector<int> &ranks) const¶ -
static Group
emptyval() noexcept¶ -
static Group
nullval() noexcept¶ Group transformation and creation functions.
union_(),intersection()ordifference()operates on the calling group instance and another groupothergroup, performs set-like operation, and returned new group instace. The ranks of processes in the new group is ordered according to their ranks in the calling group. In theunion_()case, if a process is not in the calling group, but inothergroup, it is appended after all processes in the calling group and ranked according to its rank inother group. The set operations may give a empty group instance, which is identical to the one returned byemptyval()(i.e., the comparison usingcompare()method givesIDENT).incl()returns a new group that includes the processes specified byranksin the original group. Ifranks.size()is zero, returns a empty group.excl(), on the other hand, excludes processes specified byranksin the original group and returns the new group.range_incl()andrange_excl()are similar toincl()andexcl(), respectively. But these two calls use triplets to specified the ranks to be included or excluded. The argument,ranks, must be{b1, e1, stride1, b2, e2, stride2, ...}, where each triplet{bk, ek, stridek}specifies processes with ranksbk,bk+stridek,bk+2*stridek, …,bk+floor[(ek-bk)/stridek]*stridek. It is valid thate < b && stridek < 0, but invalid thatstridek = 0.emptyval()returns an empty group.nullval()returns a null group. Note that an empty group is different from a null group - the former is a valid group instance, the later is a invalid one that cannot be used as an argument of many functions.
Example:
The following codes show how to create a new process group from a existing group:
HIPP::MPI::Env env; auto comm = env.world(); auto group = comm.group(); auto new_group = group.incl({0,1,2}); if( comm.rank() == 0 ) cout << group << new_group;
Starting from the world communicator returned by
Env::world(), a call ofComm::group()gives the group that contains all the processes. By usingGroup::incl(), the first three processes are picked out to give a new group. The information of the old and new groups is printed. Outputs are (run with 6 processes)PP::MPI::Group instance [loc=0x7ffeba41f750, size=16, align=8] ---------- Size info (size=6, rank=0) HIPP::MPI::Group instance [loc=0x7ffeba41f760, size=16, align=8] ---------- Size info (size=3, rank=0)
Note that you can get the same result by using
auto new_group = group.range_incl({0,2,1})instead of theincl().-
void
Class Requests: Non-blocking Handler¶
-
class
Requests: public MPIObj<_Requests>¶ The high-level MPI requests interface.
A request is returned by a non-blocking communication call. A
Requestsobject host an array of requests (internally, an array ofMPI_Requests). The reason of allowing one object hosting an array of requests, not just a single request , is that the later may cause overhaed in the multiple-completion call on requests.The
Requestsobject can be copy-constructed, copy-assigned, move-constructed and move-assigned. The copy operation gives a object that refers to the same array of requests. The destructor isnoexcept.-
Requests()¶ Default constructor - construct an empty array of requests. User may later put new requests into the instance by
Requests::put()orRequests::operator+=().
-
void
free()¶ -
void
clear()¶ free()frees all requests in this instance, and set the current instance to a null value as returned byRequests::nullval(). For persistent requests in the array of requests,free()frees them (so, make sure that they are completed by completion calls). For other types of requests,free()requires that they are already completed as become null values.clear()is similar tofree(), but it sets the current instance to an empty request array. The difference is that the null value is a length-1 request array, but an empty array is length-0.
-
ostream &
info(ostream &os = cout, int fmt_cntl = 1) const¶ -
friend ostream &
operator<<(ostream &os, const Requests &rqs)¶ info()displays some basic information of the requests instance toos.- Parameters
fmt_cntl – Control the display format. 0 for inline information and 1 for a verbose, multiple-line information.
- Returns
The argument
osis returned.
The overloaded << operator is equivalent to
info()with defaultfmt_cntl.The returned reference of
osallows you to chain the outputs, such asrequests.info(cout) << " continue printing " << std::endl.
-
mpi_t
raw(int i) const¶ -
bool
is_null() const¶ -
bool
is_null(int i) const¶ -
int
size() const¶ -
bool
empty() const¶ Inquery the information of the current request array.
is_null(i)tests whether the i-th request in the array is a null value,is_null()without an argument is equivalent tois_null(0).size()returns the number of requests in the array.empty()tests whether the array is empty.
-
static Requests
nullval() noexcept¶ Return a null value, which is a length-1 request array with the only element to be a null value (Internally
MPI_REQUEST_NULL).
-
void
put(Requests &rqs)¶ -
void
put(Requests &&rqs)¶ -
Requests &
operator+=(Requests &rqs)¶ -
Requests &
operator+=(Requests &&rqs)¶ -
Requests
get(int i)¶ -
Requests
get(int b, int e)¶ put()transfers the requests inrqsinto the calling instance (appended at the tail of the; order is kept). rqs becomes empty.Overloaded operator
+=is equivalent to put().get()does the opposite thing, extracting the request(s) in the current instance and return them.get(i)returns the i-th request, and get(b, e) returns a range of requests indexed in the range [b, e). After the return ofget(), the returned requests are removed from the caller instance, the hole is filled by the tail elements remaining in the caller instance (the order may change).
-
Status
wait()¶ -
Status
wait(int i)¶ -
Status
test(int &flag)¶ -
Status
test(int i, int &flag)¶ -
Status
status(int &flag) const¶ -
Status
status(int i, int &flag) const¶ -
Status
waitany(int &index)¶ -
Status
testany(int &index, int &flag)¶ -
void
waitall(vector<Status> &statuses)¶ -
void
testall(int &flag, vector<Status> &statuses)¶ -
void
waitsome(int &count, vector<int> &indices, vector<Status> &statuses)¶ -
void
testsome(int &count, vector<int> &indices, vector<Status> &statuses)¶ Completion calls of the request(s). Please refer to the MPI Standard for detailed semantics.
wait()without argument is equivalent towait(0).test(flag)is equivalent totest(0, flag).status(flag)is equivalent tostatus(0, flag). The status call returnsflag=trueif the communication is complete, and returns the aStatusobject that describes the status of such. Otherwise it setsflag=false. The status call differs from the test/wait call in that it does not deallocate or inactivate the request.
-
Class Status: Return Status¶
-
class
Status¶ Communication status record.
The
Statusclass is binary-compatible with Standard MPI_Status, i.e., a conversion fromStatus *toMPI_Status *is always valid. This design is to reduce the overhead when waiting/testing multiple messages in the non-block communications.The
Statusobject can be copy-constructed, copy-assigned, move-constructed and move-assigned. The copy operation gives a object that has the same communication status record. The destructor isnoexcept.The default constructor of
Statusgives an object with uninitialized status record.-
int
source() const noexcept¶ -
int
tag() const noexcept¶ -
int
error() const noexcept¶ -
int
count(const Datatype &dtype) const¶ -
int
count(const string &dtype) const¶ -
bool
test_cancelled() const¶ Inquery the message properties.
source()gives the rank of srouce process,tag()gives the tag of the matched message,error()gives the error code,count()counts the data item, andtest_cancelled()returns true if the message request is cancelled.The error code is set only when a multiple-completion call failed and an
ERR_IN_STATUSis returned.- Parameters
dtype – pre-defined or derived datatype. Signature of
dtypemust match the datatype used in the communication that returns this status. Only pre-defined datatypes support the string version (seeDatatype).
-
int