The elements in a collection have a global name space. That is, any processor object may refer to any element of any collection by using the elements integer Ident field or by array index notation in the case of collections that are derived from the DistributedArray collection class. To illustrate this, let us consider the parallel code that would implement a reduction of all elements of a collection. Over the next four subsections we will look at four ways this might be done. In the first scheme we will do a serial reduction from the main thread. In the second scheme we build a binary reduction tree from the elements and leave the total with element 0. We will make the following assumption. Each element in the collection has an addition operator and an accumulation operator defined as follows.
class E{
public:
E(){ // a ``zero'' initializer }
E operator +(E &);
E & operator +=(E &);
};
The serial solution looks like this.
Processor_Main(){
Processors P;
Distribution d( .... );
Align a( .....)
C < E > x(d,a); // x is the collection
E total;
for(i = 0; i < x.dim1size; i++);
total += *x(i); // x(i) returns pointer to element
};
The field
dim1size is inherited from SuperKernel collection and denotes
the size of the first dimension of the collection.The operator ( int
) returns a pointer to the
x(i)->y = 3.14;
the element must reside in the address space of the executing thread.
(This is because if the element is not local, the pointer x(i)
will be pointing to a copy and the copy will be modified.
In a similar manner, if f() is a member function of the
element class that side effects the object, then the call
x(i)->f();
will have no effect unless the element is local. There is a special
kernel function that users may use to tell if an element is local
to the executing thread:
x.Is_Local(i)
returns true if the There is one very important property of the x(i) operator that must always be kept in mind. If an element i in a collection x is not local to the thread that executes x(i), then x(i) points to a copy of the element. This copy should be viewed as a ``cached'' copy in a one element cache. Consequently, if a thread tries to access two non-local elements then only one may reside in the cache at any time. In other words, x(j) is a reference to another element after the reference to x(i), then the second reference will eliminate the first. So, for example, in the code below, the second reference to x(i) is an error.
ElementType *p;
p = x(i);
printf(" %d", x(j)->Ident); // x(j) overwrites *p
printf(" %d", p->Ident); // error: p now points to x(j)
There are ways to avoid this problem by using the function
Get_CopyElem() which is described in section 4.7.
In version 2.0 of pC++ we will change some of these rules. A new data type modifier global will be added. This idea comes from the CC++ language. The idea is that any data object that is of global type may be referenced by pointers of the form
global ElementType *p;
In this case expressions of the form p->y = 3.14 or p->f()
will cause a message to be sent to the processor object thread that contains
the element to perform the given action on the element. Additional information
on element communication can be found in section 4.7.