Example : Barrier.C

Classes/Techniques Demonstrated :

Barrier.C

The most basic group synchonization operation is the barrier. A group of threads can synchronize at a point in code by calling a barrier, which forces them to halt until the rest of the threads in the group have also called the barrier. This is accomplished in the following steps:We first allocate an object of type HPCxx_Group and set the number of threads to the maximum number that will participate in the operation. For example, to set the thread count on the main group to be 13 we can write the following.
HPCxx_Group* g;
// Code to allocate group goes here
g->setThreadCount(13);
HPCxx_Barrier barrier(g);
As shown above, a group object is associated with each HPCxx_Barrier. This can be accomplished in three ways: The constructor for the barrier takes a reference to the Group object.

Each thread that will participate in the barrier operation must then acquire a key from the barrier object with the getKey() function. (Threads do not have a natural integer index, so the key serves to enumerate threads associated with a group. While order is not essential for the operation of a barrier, it is important for reductions and scan operations.)
This example demonstrates the use of a barrier to synchronize a number of threads which are all working to fill in a unique section of an array, the address of which they all share. Once every thread has filled in its section then each thread can independently verify the sum of the values in the array. This shows (beyond something heretically flukey) that they do each have access to the same array.
#include <iostream.h>
#include <hpcxx_rts.h>

class MyThread : public HPCxx_Thread {
  HPCxx_Barrier& barrier;
  int* infoAr;
  int totalThreads, myId, count, myKey;
  
public:
  MyThread(int n, int id, int* ar, HPCxx_Barrier& _barrier):
    totalThreads(n), myId(id), infoAr(ar), barrier(_barrier),
    HPCxx_Thread() { }

  void run()
  {
    int total = 0;
    
    myKey = barrier.getKey();
    cout << "Thread " << myId << " generating array values..." << endl;

    for(count = 10 * myId; count < 10 * (myId + 1); count++) {
      infoAr[count] = (int)(1000 * drand48());
    }
    
    cout << "Thread " << myId << " finished and entering barrier..." << endl;

    // wait here until all threads are finished with array generation
    barrier(myKey);

    // sum shared array to show that all threads have same array in same state
    for(count = 0; count < 10 * totalThreads; count++) {
      total += infoAr[count];
    }
    cout << "Thread  " << myId << " computed sum : " << total << endl;
    
  } 
};

int run_threads(int n, HPCxx_Barrier barrier)
{
  MyThread* t[n];
  int* infoAr = new int[n * 10];
  int count = 0, key0;

  // give the main process a key to the barrier
  key0 = barrier.getKey();

  for(int i = 0; i < n; i++) {
    t[i] = new MyThread(n, i, infoAr, barrier);
    t[i]->start();
  }

  // wait here until all of the threads are finished
  barrier(key0);
  for(int k = 0; k < n; k++)
    t[k]->join();

  for(count = 0; count < n * 10; count++) {
    cout << "Array value " << count << " is " <<  infoAr[count] 
         << endl << flush;
  }
  return 0;
}


int main(int argc, char** argv)
{
  HPCxx_Group* g;
  hpcxx_init(argc, argv, g);
  int num_threads;
  cout << "enter number of threads: ";
  cin >> num_threads;
  cout << "using " << num_threads << " threads." << endl << flush;
  g->setNumThreads(num_threads + 1);
  HPCxx_Barrier barrier(*g);

  run_threads(num_threads, barrier);
  cout << "DONE" << endl << flush;
  hpcxx_exit(g);
  return 0;
}

Benjamin Temko

Last modified: Tue Jul 7 11:08:52 MDT 1998