A reduction applies a single operation (e.g. add, max, min, ...) to data items scattered across many processors and collects the result in one place. CHARM++ supports reductions over the members of an array or group.
The data to be reduced comes from a call to the member contribute method:
This call contributes nBytes bytes starting at data to the reduction type (see reduction types, below). Unlike sending a message, you may use data after the call to contribute. All members must call contribute, and all must use the same reduction type.
When you create a new member, it is expected to contribute to the next reduction not already in progress on that processor. The reduction will complete properly even if members are migrated or deleted during the reduction.
For example, if we want to sum each member's single integer myInt, we would use:
The built-in reduction types (see below) can also handle arrays of numbers. For example, if each element of an array has a pair of doubles forces[2] which need to be summed up (separately) across every element, from each element call:
Note that since C++ arrays (like forces[2]) are already pointers, we don't use &forces.
The result of the reduction operation is passed to the reduction client. Many different kinds of reduction clients can be used, as explained below (Section 3.14.1).
After the data is reduced, it is passed to a you via a callback object, as described in section 3.15. The message passed to the callback is of type CkReductionMsg. The important members of CkReductionMsg are getSize(), which returns the number of bytes of reduction data; and getData(), which returns a ``void *'' to the actual reduced data.
You may pass the client callback as an additional parameter to contribute. If different contribute calls pass different callbacks, some (unspecified, unreliable) callback will be chosen for use.
If no member passes a callback to contribute, the reduction will use the default callback. You set the default callback for an array or group using the ckSetReductionClient proxy call on processor zero. Again, a CkReductionMsg message will be passed to this callback, which must delete the message when done.
So, for the previous reduction on chare array arr:
and the actual entry point:
(See pgms/charm++/RedExample for a complete example).
For backward compatability, rather than a general callback you can specify a peculiar kind of C function using ckSetReductionClient or setReductionClient. This C function takes a user-defined parameter (passed to setReductionClient) and the actual reduction data, which it must not deallocate.
CHARM++ includes several built-in reduction types, used to combine the separate contributions. Any of them may be passed as an CkReduction::reducerType type to contribute.
The first four reductions (sum, product, max, and min) work on int, float, or double data as indicated by the suffix. The logical reductions (and, or) only work on integer data. All the built-in reductions work on either single numbers (pass a pointer) or arrays- just pass the correct number of bytes to contribute.
CkReduction::set returns a collection of CkReduction::setElement objects, one per contribution. This class has definition:
To extract the contribution of each array element from a reduction set, use the next routine repeatedly:
The reduction set order is undefined. Add a source field to your contribution if you need to know which array element gave a particular contribution. This will require you to do your own serialize/unserialize operation on your element structure if your reduction element data is complex. Consider using the PUP interface see 3.16 to simplify your object serialization needs.
If your data is order dependant, or if your data is just too heterogenous to be handled elegantly by the predefined types and you don't want to undertake multiple reductions, it may be best to define your own reduction type. See the next section (Section 3.14.3) for details.
It is possible to define a new type of reduction, performing a user-defined operation on user-defined data. A reduction function combines separate contributions (from this or other processors) into a single combined value.
The input to a reduction function is a list of CkReductionMsgs. A CkReductionMsg is a thin wrapper around a buffer of untyped data to be reduced. The output of a reduction function is a single CkReductionMsg containing the reduced data, which you should create using the CkReductionMsg::buildNew(int nBytes,const void *data) method.
Thus every reduction function has the prototype:
For example, a reduction function to add up contributions consisting of two machine short integers would be:
You must register your reduction function with CHARM++ using CkReduction::addReducer from an initcall routine (see section 3.18 for details on the initcall mechanism). CkReduction::addReducer returns a CkReduction::reducerType which you can later pass to contribute. Since initcall routines are executed once on every node, you can safely store the CkReduction::reducerType in a global or class-static variable. For the example above:
Note that you cannot call CkReduction::addReducer from anywhere but in an initcall routine.
June 29, 2008
Charm Homepage