Example : Reduct.C

Classes/Techniques Demonstrated :

Reduct.C

The reduction operation is used to gather values or sets of values from a number of threads in an HPCxx_Group and perform some operation on them. The computed value is then returned to each thread without that thread ever having to know the value of the other threads' contributions. In order to define a reduction you must first define a class which will perform the actual computation. This class need only have one public member function, operator(), which does the actual computation desired. For example, to create an object that can be used to form the sum-reducation of one integer from each thread, the declaration is:

class intAdd {
   public:
      int & operator(int &x, int &y) { x+=y; return x; }
};
      
Note that the first variable's value is changed. This is done to avoid having to create temporary values and buffers during the computation. It is necessary for every class you create to also place the return value into the first variable passed. The code below shows this particular reduction in use. It is similar to the HPCXX_Barrier and HPCXX_Collective examples, except that each thread doesn't ever get the whole array, it creates only its own local array, and uses the sum-reduction technique to get the computed total of all the threads' arrays.

Notice there are two calls to the reduction. The first call is done after the thread has done its own addition of its array values, and so it need only send this computed total to the reduction. The second call sends the entire array to the reduction and lets the reduction itself handle the collapsing of the array into one value before it performs the final reduction.


class intAdd {
public:
  int & operator()(int &x, int &y) { x += y; return x; }
};

class MyThread : public HPCxx_Thread {
  HPCxx_Barrier &barrier;
  HPCxx_Reduct1<int, intAdd> &add;
  int *infoAr;
  int totalThreads, myId, count, myReductKey;
  
public:
  MyThread(int n, int id, HPCxx_Reduct1<int, intAdd> &r):
    totalThreads(n), myId(id), add(r), HPCxx_Thread()
  {
      add.synchronousSetUp();
      myReductKey=add.getKey();
  }
  
  void run() {
    int myTotal=0, total=0;
    infoAr=new int[10];
    
    srand48((int)infoAr);
    cout << "Thread " << myId << " generating array values..." << endl;
 
    for(count=0; count<10; count++) {
      infoAr[count]=(int)(1000*drand48());
    }
    
    cout << "Thread " << myId << " finished and calling reduction..." << endl;

    for(count=0; count<10; count++) {
      myTotal+=infoAr[count];
    }

    total=add(myReductKey, myTotal);
    
    cout << "Thread " << myId << " received sum " << total << endl;

    cout << "Thread " << myId << " now calling reduction on my array" << endl;

    // The arguments are : key, array, number of array elements
    total=add(myReductKey, infoAr, 10);

    cout << "Thread " << myId << " received sum " << total << endl;
  } 
};

int foo(int n, HPCxx_Reduct1<int, intAdd> r){
  MyThread *t[100];
  int total=0, count=0;
  intAdd g;
  
  
  for(int i = 0; i < n; i++){
    t[i] = new MyThread(n, i, r);
    t[i]->start();
  }  

  cout << "DONE" << endl << flush;
  return 0;
}


int main(int argc, char **argv)
{
  HPCxx_Group *g;
  int n;

  hpcxx_init(argc, argv, g);

  cout << "INPUT" << endl << flush;
  cout << "Number of threads : " << endl << flush;
  cin >> n;
  g->setNumThreads(n);
  HPCxx_Reduct1<int, intAdd> r(*g);
  foo(n, r);
  return hpcxx_exit(g);
}
      

hpc++@extreme.indiana.edu

Last modified: Thu Apr 22 02:12:46 EST 1999