POOMA: A C++ Toolkit for High-Performance Parallel Scientific Computing | ||
---|---|---|
Prev | Chapter 5. Array Containers | Next |
A POOMA Array maps Domain indices to values. In this section, we describe how to declare Arrays. In the next section, we explain how to access individual values stored within an Array and how to copy Arrays.
Array values need not just be stored values, as C arrays have. The values can also be computed dynamically by the engine associated with the Array. We defer discussion of computing values to the next chapter discussing engines (Chapter 6). Therefore, when we mention "the values stored in an Array", we implicitly mean "the values stored in or computed by the Array".
Declaring an Array requires four arguments: the domain's dimensionality, the type of values stored or computed, a specification how the values are stored or computed, and a Domain. The first three arguments are template parameters since few scientific programs need to (and no POOMA programs can) change these values while a program executes. For example, an Array cannot change the type of the values it stores, but an Array's values can be copied into another Array having the desired type. Although scientific programs do not frequently change an array's domain, they do frequently request a subset of the array's values, i.e., a view. The subset is specified via a Domain so it is a run-time value. Views are presented in Chapter 8.
An Array's first template parameter specifies its dimensionality. This positive integer D specifies its rank and has the same value as its domain's dimensionality. Theoretically, an Array can have any positive integer, but the POOMA code currently supports a dimensionality of at most seven. For almost all scientific codes, a dimension of three or four is sufficient, but the POOMA code can be extended to support higher dimensions.
An Array's second template parameter specifies the type of its stored or computed values. Common value types include int, double, complex, and Vector, but any type is permissible. For example, an Array's values might be matrices or even other Arrays. The parameter's default value is usually double, but it may be changed when the POOMA Toolkit is configured.
An Array's third parameter specifies how its data is stored or computed by an Engine and its values accessed. The argument is a tag indicating a particular type of Engine. Permissible tags include Brick, CompressibleBrick, and ConstantFunction. The Brick tag indicates all Array values will be explicitly stored, just as built-in C arrays do. If an Array frequently stores exactly the same value in every position, a CompressibleBrick Engine, which reduces its space requirements to a constant whenever all its values are the same, is appropriate. A ConstantFunction Engine returns the same value for all indices. Some Engines compute values, e.g., applying a function to every value in another Engine. These Engines are discussed in Chapter 6. To avoid being verbose in the rest of this chapter, we abbreviate "store or compute values" as "store values". The engine parameter's default value is usually Brick, but it may be changed when the POOMA Toolkit is configured.
Even though every Array container has an engine to store its values and permit access to individual values, the concept of an Array is conceptually separate from the concept of an engine. An engine's role is low-level, storing values and permitting access to individual values. As we indicated above, the storage can be optimized to fit specific situations such as few nonzero values and computing values using a function applied to another engine's values. An Array's role is high-level, supporting access to groups of values. Arrays can be used in data-parallel expressions, e.g., adding all the values in one Array to all the values in another. (See Chapter 7 for more information.) Subsets of Array values, frequently used in data-parallel statements, can be obtained. (See Chapter 8 for more information.) Even though engines and Arrays are conceptually separate, higher-level Arrays provide access to lower-level Engines. Users usually have an Array create its Engine(s), rarely explicitly creating Engines themselves. Also, Arrays support access to individual values. In short, POOMA users use Arrays, only dealing with how they are implemented (engines) when declaring them. For a description of Engines, see Chapter 6.
An Array's one run-time argument is its domain. The domain specifies its extent and consequently how many values it can return. All the provided Domain objects are combined to yield an Interval<D>, where D matches the Array's first template parameter. Since an Interval domain with its unit strides is used, there are no unaccessed "gaps" within the domain, wasting storage space. To use other domains to access an Array, first create it using an Interval domain and then take a view of it, as described in Chapter 8. As we mentioned above, the current POOMA code supports up to seven dimensions so at most seven Domain objects can be provided. If more dimensions are required, the POOMA code can be extended to the desired number of dimensions.
Array constructors are listed in Table 5-10. An Array's three template parameters for dimensionality, value type, and engine type are abbreviated D, T, and E. Template parameters for domain types are named DT1, …, DT7. The first constructor, with no domain arguments, creates an empty, uninitialized Array for which a domain must be specified before it is used. Specify the array's domain using its initialize function. The next seven constructors combine their domain arguments to compute the resulting Array's domain. These are combined in the same way that multidimensional Intervals are constructed. (See Table 5-4 and the following text.) The domain objects, having types DT1, …, DT7, can have any type that can be converted into an integer, into a single-dimensional Domain object that can be converted into a single-dimensional Interval, or to a multidimensional Domain object that itself can be converted into an Interval. The total dimensionality of all the arguments' types should equal D, unlike Interval construction which permits total dimensionality less than or equal to D. One-dimensional Domain objects that can be converted into one-dimensional Intervals include Loc<1>s, Interval<1>s, and Range<1>s with unit strides. To initialize all of an Array's values to a specific value, use one of the final seven constructors, each taking a particular value, wrapped as a ModelElement. These constructors use the given domain objects the same way as the preceding constructors but assign model to every Array value. model's type is ModelElement<T>, rather than T, to differentiate it from an int, which can also be used to specify a domain object. ModelElement just stores an element of any type T, which must match the Array's value type T.
Table 5-10. Declaring Arrays
Array declaration | result |
---|---|
Array<D,T,E>() | creates an empty, uninitialized Array which must be initialize()d before use. |
Array<D,T,E>(const DT1& t1) | creates an Array using the given Domain object or integer. |
Array<D,T,E>(const DT1& t1, const DT2& t2) | creates an Array using the given Domain objects and integers. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3) | creates an Array using the given Domain objects and integers. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4) | creates an Array using the given Domain objects and integers. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5) | creates an Array using the given Domain objects and integers. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6) | creates an Array using the given Domain objects and integers. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6, const DT7& t7) | creates an Array using the given Domain objects and integers. |
Array<D,T,E>(const DT1& t1, const ModelElement<T>& model) | creates an Array using the given Domain object or integer and then initializes all entries using model. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const ModelElement<T>& model) | creates an Array using the given Domain objects and integers and then initializes all entries using model. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const ModelElement<T>& model) | creates an Array using the given Domain objects and integers and then initializes all entries using model. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const ModelElement<T>& model) | creates an Array using the given Domain objects and integers and then initializes all entries using model. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const ModelElement<T>& model) | creates an Array using the given Domain objects and integers and then initializes all entries using model. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6, const ModelElement<T>& model) | creates an Array using the given Domain objects and integers and then initializes all entries using model. |
Array<D,T,E>(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6, const DT7& t7, const ModelElement<T>& model) | creates an Array using the given Domain objects and integers and then initializes all entries using model. |
Template parameters D, T, and E indicates the Array's dimension, value type, and Engine type, respectively. DT1, …, DT7 indicate domain types or integers. |
We illustrate creating Arrays. To create a three-dimensional Array a explicitly storing double floating-point values, use
Interval<1> D(6); Interval<3> I3(D,D,D); Array<3,double,Brick> a(I3);The template parameters specify its dimensionality, the type of its values, and a Brick Engine type, which explicitly stores values. Its domain, which must have three dimensions, is specified by an Interval<3> object which consists of [0,5] intervals for all its three dimensions. Since double and Brick are usually the default template parameters, they can be omitted so these declarations are equivalent:
Array<3,double> a_duplicate1(I3); Array<3> a_duplicate2(I3);.To create a similar Array with a domain of [0:1:1, 0:2:1, 0:0:1], use
Array<3> b(2,3,1);since specifying an integer i indicates a one-dimensional zero-based Interval [0:i-1:1]. To store booleans, specify bool as the second template argument:
Array<2,bool> c(2,3);To specify a default Array value of true, use ModelElement<bool>(true):
Array<2,bool> c(2,3, ModelElement<bool>(true));.To create a one-dimensional Array containing seven doubles all equaling π, use
const double pi = 4.0*atan(1.0); Array<1,double,CompressibleBrick> d(7, ModelElement<double>(pi));.We use a CompressibleBrick Engine, rather than a Brick Engine, so all seven values will be stored in one location rather than in seven separate locations when they are all the same.
An uninitialized Array, created using its parameter-less constructor, must have a specified domain before it can be used. For example, one must use the parameter-less Array constructor when creating an array of Arrays using new so their domains must be specified. (It would probably be better to create an Array of Arrays since memory allocation and deallocation would automatically be handled.) Array's initialize functions accept the same set of domain object specifications and model elements that the Array constructors do, creating the specified domain. See Table 5-11. For example, both a and b are two-dimensional Arrays of floats with a [2:7:1,-2:4:1] domains:
// Create an Array and its domain. Array<2,float,Brick> a(Interval<1>(2,7), Interval<1>(-2,4)); // Create an Array without a domain and then specify // its domain. Array<2,float,Brick> b(); b.initialize(Interval<1>(2,7), Interval<1>(-2,4));.Invoking initialize on an Array with an existing domain yields unspecified behavior. All Array values may be lost and memory may be leaked.
Table 5-11. Initializing Arrays' Domains
An Array's initialize member functions sets its domain and should be invoked only for an array created without a domain. It returns nothing. | |
---|---|
initialize declaration | result |
initialize(const DT1& t1) | creates the Array's domain using the given Domain object or integer. |
initialize(const DT1& t1, const DT2& t2) | creates the Array's domain using the given Domain objects and integers. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3) | creates the Array's domain using the given Domain objects and integers. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4) | creates the Array's domain using the given Domain objects and integers. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5) | creates the Array's domain using the given Domain objects and integers. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6) | creates the Array's domain using the given Domain objects and integers. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6, const DT7& t7) | creates the Array's domain using the given Domain objects and integers. |
initialize(const DT1& t1, const ModelElement<T>& model) | creates the Array's domain using the given Domain object or integer and then initializes all entries using model. |
initialize(const DT1& t1, const DT2& t2, const ModelElement<T>& model) | creates the Array's domain using the given Domain objects and integers and then initializes all entries using model. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const ModelElement<T>& model) | creates the Array's domain using the given Domain objects and integers and then initializes all entries using model. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const ModelElement<T>& model) | creates the Array's domain using the given Domain objects and integers and then initializes all entries using model. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const ModelElement<T>& model) | creates the Array's domain using the given Domain objects and integers and then initializes all entries using model. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6, const ModelElement<T>& model) | creates the Array's domain using the given Domain objects and integers and then initializes all entries using model. |
initialize(const DT1& t1, const DT2& t2, const DT3& t3, const DT4& t4, const DT5& t5, const DT6& t6, const DT7& t7, const ModelElement<T>& model) | creates the Array's domain using the given Domain objects and integers and then initializes all entries using model. |
Template parameters DT1, …, DT7 indicate domain types or integers. |