Reductions are operations for which a message (or user data structure) is contributed by each partecipant processor. All these contributions are merged according to a merge-function provided by the user. A Converse handler is then invoked with the resulting message. Reductions can be on the entire set of processors, or on a subset of the whole. Currently reductions are only implemented on processors sets. No equivalent exists for SMP nodes.
There are eight functions used to deposit a message into the system, summarized in Table 2.1. Half of them receive as contribution a Converse message (with a Converse header at its beginning). This message must have already been set for delivery to the desired handler. The other half (ending with ``Struct'') receives a pointer to a data structure allocated by the user. This second version may allow the user to write a simpler merging function. For instance, the data structure could be a tree that can be easily expanded by adding more nodes.
The signatures for the functions in Table 2.1 are:
void CmiReduce(void *msg, int size, CmiReduceMergeFn mergeFn);
void CmiReduceStruct(void *data, CmiReducePupFn pupFn,
CmiReduceMergeFn mergeFn, CmiHandler dest,
CmiReduceDeleteFn deleteFn);
void CmiReduceID(void *msg, int size, CmiReduceMergeFn mergeFn, CmiReductionID id);
void CmiReduceStructID(void *data, CmiReducePupFn pupFn,
CmiReduceMergeFn mergeFn, CmiHandler dest,
CmiReduceDeleteFn deleteFn, CmiReductionID id);
void CmiListReduce(int npes, int *pes, void *msg, int size, CmiReduceMergeFn mergeFn, CmiReductionID id);
void CmiListReduceStruct(int npes, int *pes,
void *data, CmiReducePupFn pupFn,
CmiReduceMergeFn mergeFn, CmiHandler dest,
CmiReduceDeleteFn deleteFn, CmiReductionID id);
void CmiGroupReduce(CmiGroup grp, void *msg, int size, CmiReduceMergeFn mergeFn, CmiReductionID id);
void CmiGroupReduceStruct(CmiGroup grp, void *data, CmiReducePupFn pupFn,
CmiReduceMergeFn mergeFn, CmiHandler dest,
CmiReduceDeleteFn deleteFn, CmiReductionID id);
In all the above, msg is the Converse message deposited by the local processor, size is the size of the message msg, and data is a pointer to the user-allocated data structure deposited by the local processor. dest is the CmiHandler where the final message shall be delivered. It is explicitly passed in ``Struct'' functions only, since for the message versions it is taken from the header of msg. Moreover there are several other function pointers passed in by the user:
void * (*mergeFn)(int *size, void *local, void **remore, int count)
Prototype for a CmiReduceMergeFn function pointer argument.
This function is used in all the CmiReduce forms to merge the local
message/data structure deposited on a processor with all the messages incoming
from the children processors of the reduction spanning tree. The input parameters
are in the order: the size of the local data for message reductions (always zero
for struct reductions); the local data itself (the exact same pointer passed in as first
parameter of CmiReduce and similar); a pointer to an array of incoming messages;
the number of elements in the second parameter. The function returns a pointer
to a freshly allocated message (or data structure for the Struct forms)
corresponding to the merge of all the messages. When performing message
reductions, this function is also responsible to updating the integer pointed by
size to the new size of the returned message. All the messages in the
remote array are deleted by the system; the data pointed by the first parameter
should be deleted by this function. If the data can be merged ``in-place'' by
modifying or augmenting local, the function can return the same pointer to
local which can be considered freshly allocated. Each element in remote
is the complete incoming message (including the converse header) for message
reductions, and the data as it has been packed by the pup function (without any
additional header) for struct reductions.
void (*pupFn)(pup_er p, void *data)
Prototype for a CmiReducePupFn function pointer argument.
This function will use the PUP framework to pup the data passed in
into a message for sending across the network. The data can be either the same
data passed in as first parameter of any ``Struct'' function, or
the return of the merge function. It will be called for sizing and packing.
(Note: It will not be called for unpacking.)
void (*deleteFn)(void *ptr)
Prototype for a CmiReduceDeleteFn function pointer argument.
This function is used to delete either the data stucture passed in as first
parameter of any ``Struct'' function, or the return of the merge
function. It can be as simple as ``free'' or as complicated as needed to delete
complex structures. If this function is NULL, the data structure will not be
deleted, and the program can continue to use it. Note: even if this function is
NULL, the input data structure may still be modified by the merge function.
CmiReduce and CmiReduceStruct are the simplest reduction function, and they reduce the deposited message/data across all the processors in the system. Each processor must to call this function exactly once. Multiple reductions can be invoked without waiting for previous ones to finish, but the user is responsible to call CmiReduce/CmiReduceStruct in the same order on every processor. (Note: CmiReduce and CmiReduceStruct are not interchangeable. Either every processor calls CmiReduce or every processor calls CmiReduceStruct).
In situations where it is not possible to guarantee the order of reductions, the user may use CmiReduceID or CmiReduceStructID. These functions have an additional parameter of type CmiReductionID which will uniquely identify the reduction, and match them correctly. (Note: No two reductions can be active at the same time with the same CmiReductionID. It is up to the user to guarantee this.)
A CmiReductionID can be obtained by the user in three ways, using one of the following functions:
CmiReductionID CmiGetGlobalReduction()
This function must be called on every processor, and in the same order if
called multiple times. This would generally be inside initialization code, that
can set aside some CmiReductionIDs for later use.
CmiReductionID CmiGetDynamicReduction()
This function may be called only on processor zero. It returns a unique
ID, and it is up to the user to distrubute this ID to any processor that needs
it.
void CmiGetDynamicReductionRemote(int handlerIdx, int pe, int dataSize, void *data)
This function may be called on any processor. The produced CmiReductionID
is returned on the specified pe by sending a message to the specified
handlerIdx. If pe is -1, then all processors will receive the
notification message. data can be any data structure that the user wants to
receive on the specified handler (for example to differentiate between
requests). dataSize is the size in bytes of data. If dataSize is
zero, data is ignored.
The message received by handlerIdx consists of the standard Converse
header, followed by the requested CmiReductionID (represented as a 4 bytes
integer the user can cast to a CmiReductionID, a 4 byte integer containing
dataSize, and the data itself.
The other four functions (CmiListReduce, CmiListReduceStruct, CmiGroupReduce, CmiGroupReduceStruct) are used for reductions over subsets of processors. They all require a CmiReductionID that the user must obtain in one of the ways described above. The user is also responsible that no two reductions use the same CmiReductionID simultaneously. The first two functions receive the subset description as processor list (pes) of size npes. The last two receive the subset description as a previously established CmiGroup (see 2.7).
November 23, 2009
Converse Homepage
Charm Homepage