mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 01:30:22 +00:00
393 lines
12 KiB
Chapel
393 lines
12 KiB
Chapel
//
|
|
// Distributions Primer
|
|
//
|
|
// This primer demonstrates uses of some of Chapel's standard
|
|
// distributions. To use these distributions in a Chapel program,
|
|
// the respective module must be used:
|
|
//
|
|
use BlockDist, CyclicDist, BlockCycDist, ReplicatedDist;
|
|
use DimensionalDist2D, ReplicatedDim, BlockCycDim;
|
|
|
|
//
|
|
// For each distribution, we'll create a distributed domain and array
|
|
// and then initialize it just to give a brief flavor of how the
|
|
// distribution maps across locales. Running this example on 6
|
|
// locales does a nice job of illustrating the distribution
|
|
// characteristics.
|
|
//
|
|
// All of these distributions support options to map to a different
|
|
// virtual locale grid than the one used by default (a
|
|
// multidimensional factoring of the built-in Locales array), as well
|
|
// as to control the amount of parallelism used in data parallel
|
|
// loops. See the Standard Distributions chapter of the language spec
|
|
// for more details.
|
|
//
|
|
|
|
//
|
|
// Make the program size configurable from the command line.
|
|
//
|
|
config const n = 8;
|
|
|
|
//
|
|
// Declare a 2-dimensional domain Space that we will later use to
|
|
// initialize the distributed domains.
|
|
//
|
|
const Space = {1..n, 1..n};
|
|
|
|
//
|
|
// The Block distribution distributes a bounding box from
|
|
// n-dimensional space across the target locale array viewed as an
|
|
// n-dimensional virtual locale grid. The bounding box is blocked
|
|
// into roughly equal portions across the locales. Note that domains
|
|
// declared over a Block distribution can also store indices outside
|
|
// of the bounding box; the bounding box is merely used to compute
|
|
// the blocking of space.
|
|
//
|
|
// In this example, we declare a 2-dimensional Block-distributed
|
|
// domain BlockSpace and a Block-distributed array BA declared over
|
|
// the domain.
|
|
//
|
|
const BlockSpace = Space dmapped Block(boundingBox=Space);
|
|
var BA: [BlockSpace] int;
|
|
|
|
//
|
|
// To illustrate how the index set is distributed across locales,
|
|
// we'll use a forall loop to initialize each array element to the
|
|
// locale ID that stores that index/element/iteration.
|
|
//
|
|
forall ba in BA do
|
|
ba = here.id;
|
|
|
|
//
|
|
// The 'hasSingleLocalSubdomain' method on arrays will return true if the
|
|
// index set for a locale can be represented by a single domain.
|
|
//
|
|
if !BA.hasSingleLocalSubdomain() then
|
|
halt("For a Block distribution, the index set per locale should be \
|
|
represented by a single domain");
|
|
|
|
//
|
|
// If the distribution's subdomains can be represented as single subdomain,
|
|
// we can use the 'localSubdomain' method to get the index set for the
|
|
// current locale.
|
|
//
|
|
// Below, we'll use the index set to confirm that the array elements have the
|
|
// correct locale id.
|
|
//
|
|
|
|
for L in Locales {
|
|
on L {
|
|
const indices = BA.localSubdomain();
|
|
for i in indices {
|
|
if BA[i] != L.id then
|
|
halt("Error: incorrect locale id");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Output the Block-distributed array to visually see how the elements
|
|
// are partitioned across the locales.
|
|
//
|
|
writeln("Block Array Index Map");
|
|
writeln(BA);
|
|
writeln();
|
|
|
|
//
|
|
// Most of Chapel's standard distributions support an optional
|
|
// targetLocales argument that permits you to pass in your own
|
|
// array of locales to be targeted. In general, the targetLocales
|
|
// argument should match the rank of the distribution. So for
|
|
// example, to map a Block to a [numLocales x 1] view of the
|
|
// locale set, one could do something like this:
|
|
|
|
//
|
|
// We start by creating our own array of the locale values. Here
|
|
// we use the standard array reshape function for convenience,
|
|
// but more generally, this array could be accessed/assigned like any
|
|
// other.
|
|
//
|
|
|
|
var MyLocaleView = {0..#numLocales, 1..1};
|
|
var MyLocales: [MyLocaleView] locale = reshape(Locales, MyLocaleView);
|
|
|
|
//
|
|
// Then we'll declare a distributed domain/array that targets
|
|
// this view of the locales:
|
|
//
|
|
|
|
const BlockSpace2 = Space dmapped Block(boundingBox=Space,
|
|
targetLocales=MyLocales);
|
|
var BA2: [BlockSpace2] int;
|
|
|
|
//
|
|
// Then we'll do a similar computation as before to verify where
|
|
// everything ended up:
|
|
//
|
|
forall ba in BA2 do
|
|
ba = here.id;
|
|
|
|
writeln("Block Array Index Map");
|
|
writeln(BA2);
|
|
writeln();
|
|
|
|
//
|
|
// We can use the 'targetLocales' method available on an array to get the
|
|
// locales array used as targets.
|
|
//
|
|
for (L, ML) in zip(BA2.targetLocales(), MyLocales) do
|
|
if L != ML then
|
|
halt("Error: BA2.targetLocales() should equal MyLocales");
|
|
|
|
|
|
|
|
//
|
|
// Next, we'll perform a similar computation for the Cyclic distribution.
|
|
// Cyclic distributions start at a designated n-dimensional index and
|
|
// distribute the n-dimensional space across an n-dimensional array
|
|
// of locales in a round-robin fashion (in each dimension). As with
|
|
// the Block distribution, domains may be declared using the
|
|
// distribution who have lower indices that the starting index; that
|
|
// value should just be considered a parameterization of how the
|
|
// distribution is defined.
|
|
//
|
|
const CyclicSpace = Space dmapped Cyclic(startIdx=Space.low);
|
|
var CA: [CyclicSpace] int;
|
|
|
|
forall ca in CA do
|
|
ca = here.id;
|
|
|
|
writeln("Cyclic Array Index Map");
|
|
writeln(CA);
|
|
writeln();
|
|
|
|
//
|
|
// The domain returned by 'localSubdomain' need not be a dense block, as is
|
|
// the case for the Cyclic Distribution.
|
|
//
|
|
on Locales[0] {
|
|
const indices = CA.localSubdomain();
|
|
for i in indices {
|
|
if CA[i] != 0 then
|
|
halt("Error: Cyclic array values on Locale 0 should be zero");
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Next, we'll declare a Block-Cyclic distribution. These
|
|
// distributions also deal out indices in a round-robin fashion,
|
|
// but rather than dealing out singleton indices, they deal out blocks
|
|
// of indices. Thus, the BlockCyclic distribution is parameterized
|
|
// by a starting index (as with Cyclic) and a block size (per
|
|
// dimension) specifying how large the chunks to be dealt out are.
|
|
//
|
|
const BlkCycSpace = Space dmapped BlockCyclic(startIdx=Space.low,
|
|
blocksize=(2, 3));
|
|
var BCA: [BlkCycSpace] int;
|
|
|
|
forall bca in BCA do
|
|
bca = here.id;
|
|
|
|
writeln("Block-Cyclic Array Index Map");
|
|
writeln(BCA);
|
|
writeln();
|
|
|
|
//
|
|
// A locale's index set for a Block-Cyclic distribution cannot be represented
|
|
// by a single subdomain.
|
|
//
|
|
if BCA.hasSingleLocalSubdomain() then
|
|
halt("A Block-Cyclic index set cannot be represented by a single subdomain");
|
|
|
|
//
|
|
// If the local index set cannot be represented by a single subdomain,
|
|
// we can use the 'localSubdomains' iterator to yield a number of domains
|
|
// that represent the whole index set.
|
|
//
|
|
// Let's write a function that will use 'localSubdomains' to verify the
|
|
// correctness of the array values.
|
|
//
|
|
|
|
proc verifyID(Data: []) {
|
|
for L in Locales {
|
|
on L {
|
|
for indices in Data.localSubdomains() {
|
|
for i in indices {
|
|
if Data[i] != L.id then
|
|
halt("Error: incorrect locale id");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
verifyID(BCA);
|
|
|
|
//
|
|
// The 'localSubdomains' iterator is also available on distributions that
|
|
// can represent a locale's index set with a single domain. This allows us to
|
|
// write more general code that will work for all distributions.
|
|
//
|
|
// This means that we can call the 'verifyID' function on any array, like the
|
|
// 'BA' array from earlier.
|
|
//
|
|
verifyID(BA);
|
|
|
|
|
|
//
|
|
// The ReplicatedDist distribution is different: each of the
|
|
// original domain's indices - and the corresponding array elements -
|
|
// is replicated onto each locale. (Note: consistency among these
|
|
// array replicands is NOT maintained automatically.)
|
|
//
|
|
// This replication is observable in some cases but not others,
|
|
// as shown below. Note: this behavior may change in the future.
|
|
//
|
|
const ReplicatedSpace = Space dmapped ReplicatedDist();
|
|
var RA: [ReplicatedSpace] int;
|
|
|
|
// The replication is observable - this visits each replicand.
|
|
forall ra in RA do
|
|
ra = here.id;
|
|
|
|
writeln("Replicated Array Index Map, ", RA.numElements, " elements total");
|
|
writeln(RA);
|
|
writeln();
|
|
|
|
//
|
|
// The replication is observable when the replicated array is
|
|
// on the left-hand side. If the right-hand side is not replicated,
|
|
// it is copied into each replicand.
|
|
// We illustrate this using a non-distributed array.
|
|
//
|
|
var A: [Space] int = [(i,j) in Space] i*100 + j;
|
|
RA = A;
|
|
writeln("Replicated Array after being array-assigned into");
|
|
writeln(RA);
|
|
writeln();
|
|
|
|
//
|
|
// Analogously, each replicand will be visited and
|
|
// other participated expressions will be computed on each locale
|
|
// (a) when the replicated array is assigned a scalar:
|
|
// RA = 5;
|
|
// (b) when it appears first in a zippered forall loop:
|
|
// forall (ra, a) in zip(RA, A) do ...;
|
|
// (c) when it appears in a for loop:
|
|
// for ra in RA do ...;
|
|
//
|
|
// Zippering (RA,A) or (A,RA) in a 'for' loop will generate
|
|
// an error due to their different number of elements.
|
|
|
|
// Let RA store the Index Map again, for the examples below.
|
|
forall ra in RA do
|
|
ra = here.id;
|
|
|
|
//
|
|
// Only the local replicand is accessed - replication is NOT observable
|
|
// and consistency is NOT maintained - when:
|
|
// (a) the replicated array is indexed - an individual element is read...
|
|
//
|
|
on Locales(0) do
|
|
writeln("on ", here, ": ", RA(Space.low));
|
|
on Locales(LocaleSpace.high) do
|
|
writeln("on ", here, ": ", RA(Space.low));
|
|
writeln();
|
|
|
|
// ...or an individual element is written;
|
|
on Locales(LocaleSpace.high) do
|
|
RA(Space.low) = 7777;
|
|
|
|
writeln("Replicated Array after being indexed into");
|
|
writeln(RA);
|
|
writeln();
|
|
|
|
//
|
|
// (b) the replicated array is on the right-hand side of an assignment...
|
|
//
|
|
on Locales(LocaleSpace.high) do
|
|
A = RA + 4;
|
|
writeln("Non-Replicated Array after assignment from Replicated Array + 4");
|
|
writeln(A);
|
|
writeln();
|
|
|
|
//
|
|
// (c) ...or, generally, the replicated array or domain participates
|
|
// in a zippered forall loop, but not in the first position.
|
|
// The loop could look like:
|
|
//
|
|
// forall (a, (i,j), ra) in (A, ReplicatedSpace, RA) do ...;
|
|
//
|
|
|
|
|
|
//
|
|
// The DimensionalDist2D distribution lets us build a 2D distribution
|
|
// as a composition of specifiers for individual dimensions.
|
|
// Under such a "dimensional" distribution each dimension is handled
|
|
// independently of the other.
|
|
//
|
|
// The dimension specifiers are similar to the corresponding multi-dimensional
|
|
// distributions in constructor arguments and index-to-locale mapping rules.
|
|
// However, instead of an array of locales, a specifier constructor
|
|
// accepts just the number of locales that the indices in the corresponding
|
|
// dimension will be distributed across.
|
|
//
|
|
// The DimensionalDist2D constructor requires:
|
|
// * an [0..nl1-1, 0..nl2-1] array of locales, where
|
|
// nl1 and nl2 are the number of locales in each dimension, and
|
|
// * two dimension specifiers, created for nl1 and nl2 locale counts, resp.
|
|
//
|
|
// Presently, the following dimension specifiers are available
|
|
// (shown here with their constructor arguments):
|
|
//
|
|
// * ReplicatedDim(numLocales)
|
|
// * BlockDim(numLocales, boundingBoxLow, boundingBoxHigh)
|
|
// * BlockCyclicDim(lowIdx, blockSize, numLocales)
|
|
//
|
|
|
|
//
|
|
// The following example creates a dimensional distribution that
|
|
// replicates over 2 locales (when available) in the first dimemsion
|
|
// and distributes using block-cyclic distribution in the second dimension.
|
|
// The example computes nl1 and nl2 and reshapes MyLocales correspondingly.
|
|
//
|
|
|
|
var (nl1, nl2) = if numLocales == 1 then (1, 1) else (2, numLocales/2);
|
|
MyLocaleView = {0..#nl1, 0..#nl2};
|
|
MyLocales = reshape(Locales[0..#nl1*nl2], MyLocaleView);
|
|
|
|
const DimReplicatedBlockcyclicSpace = Space
|
|
dmapped DimensionalDist2D(MyLocales,
|
|
new ReplicatedDim(numLocales = nl1),
|
|
new BlockCyclicDim(numLocales = nl2,
|
|
lowIdx = 1, blockSize = 2));
|
|
|
|
var DRBA: [DimReplicatedBlockcyclicSpace] int;
|
|
|
|
// The ReplicatedDim specifier always accesses the local replicand.
|
|
// (This differs from how the ReplicatedDist distribution works.)
|
|
//
|
|
// This example visits each replicand. The behavior is the same
|
|
// regardless of the second index into MyLocales below.
|
|
|
|
for locId1 in 0..#nl1 do on MyLocales[locId1, 0] {
|
|
|
|
forall drba in DRBA do
|
|
drba = here.id;
|
|
|
|
writeln("Dimensional2D(Replicated,BlockCyclic) Array Index Map",
|
|
" from ", here);
|
|
|
|
// Technicality: 'writeln(DRBA)' would read DRBA always on Locale 0.
|
|
// Since we want to see what DRBA contains on the current locale,
|
|
// we use 'Helper' that is mapped using the default distribution.
|
|
// 'Helper = DRBA' captures the view of DRBA on the current locale,
|
|
// which we then print out.
|
|
|
|
const Helper: [Space] int = DRBA;
|
|
writeln(Helper);
|
|
writeln();
|
|
|
|
}
|