There are three main families of collective operation in Adlib: regular communications, reduction operations, and irregular communications.
The regular communications are exemplified the operations shift, cshift, writeHalo and remap, introduced in earlier sections. The last of these, remap, is a very characteristic example. The remap function takes two distributed array arguments--a source array and a destination. These two arrays must have the same size and shape 6.1but they can have any, unrelated, distribution formats. The effect of the operation is to copy the values of the elements in the source array to the corresponding elements in the destination array, performing any communications required to do that. If the destination array has replicated mapping, the remap operation will broadcast source values to all copies of the destination array elements.
The remap function is a static member of the Adlib class. Like most of the functions in Adlib, the remap function is overloaded to apply to various ranks and types of array:
void remap(int [[]] destination, int [[]] source) ; void remap(float [[]] destination, float [[]] source) ; void remap(double [[]] destination, double [[]] source) ; ... void remap(int [[,]] destination, int [[,]] source) ; void remap(float [[,]] destination, float [[,]] source) ; void remap(double [[,]] destination, double [[,]] source) ; ... void remap(int [[,,]] destination, int [[,,]] source) ; void remap(float [[,,]] destination, float [[,,]] source) ; void remap(double [[,,]] destination, double [[,,]] source) ; ...and scalars:
void remap(int # destination, int # source) ; void remap(float # destination, float # source) ; void remap(double # destination, double # source) ; ...Currently the element-type overloading includes all Java primitive types. Later Adlib will be extended to support Object types.
There are four preconditions for a call to remap:
Most of the functions in Adlib have a similar set of preconditions--all operations are called collectively with coherent arguments, input and output arrays should never overlap, and array arguments must always be fully contained in the active group. The last requirement is probably the easiest to overlook. Consider the example of section 3.3, Figure 3.15. An easy mistake would be to put the calls to remap inside the following on construct. This is an error, because there is no guarantee that distribution groups of a and b are contained in the distribution group, p, of c. The function matmul is supposed to work for arguments with any, unrelated, distribution format. The Adlib library includes runtime checks for containment of arrays. If an argument is not fully contained, an exception occurs.
So long as the rule on containment is observed, Adlib calls can be made freely inside distributed control constructs, including the parallel loop, overall. If, for example, we want to ``skew'' an array--shift rows in the y direction by an amount that depends on the x index--we can do something like
on(p) { int [[,]] a = new int [[x, y]], b = new int [[x, y]] ; overall(i = x for :) Adlib.shift(b [[i, :]], a [[i, :]], i`) ; }The section arguments of shift have distribution group p / i, which is identical to the active process group at this point, so the arguments are fully contained. A slightly more complicated example involving dotProduct was given earlier in section 4.5, Figure 4.6.
A prototype of the shift function was given in section 2.3. In general we have
wherevoid shift(
[[]] destination,
[[]] source,
int shiftAmount) ;
void shift(
[[,]] destination,
[[,]] source,
int shiftAmount, int dimension) ;
void shift(
[[,,]] destination,
[[,,]] source,
int shiftAmount, int dimension) ;
...
The shift operation discards values from source that are moved past the edge of destination. At the other end of the range, elements of destination that are not targetted by elements from source are unchanged from their input value. The operation cshift is essentially identical to shift except that it implements a circular shift.
The function writeHalo is applied to distributed arrays that have ghost regions. It updates those regions. The simplest versions have prototypes
We can distinguish between the locally held physical segment of an array and the surrounding ghost region, which is used to cache local copies of remote elements. The effect of writeHalo is to overwrite the ghost region with values from processes holding the corresponding elements in their physical segments.void writeHalo(
[[]] a) ;
void writeHalo(
[[,]] a) ;
void writeHalo(
[[,,]] a) ;
...
A more general form of writeHalo allows to specify that only a subset of the available ghost area is to be updated, and to select circular wraparound for updating ghost cells at the extreme ends of the array, if desired.
The three integer vectors are all of lengthvoid writeHalo(
[[]] a, int wlo [], int whi [], int [] mode) ;
void writeHalo(
[[,]] a, int wlo [], int whi [], int [] mode) ;
void writeHalo(
[[,,]] a, int wlo [], int whi [], int [] mode) ;
...
Operation of writeHalo is visualized in figure 6.1.
[Need to do something about Adlib.copy. Probably this should be moved into a standard HPJava library.]