0

I am writing a C library with routines I want to call from an external driver program using many OpenMP threads. The library will have a main container called lib_workspace, which will be used to do intermediate computations. Just wondering which of these is the better option:

Option 1:

In this case, I define multiple copies of everything in lib_workspace for different threads:

typedef struct
{
  double a[MAX_THREADS];
  double b[MAX_THREADS];
} lib_workspace;

double lib_calc_something(double x, lib_workspace * work)
{ 
  int thread_id = omp_get_thread_num();
  work->a[thread_id] = x - 2.0;
  work->b[thread_id] = x + 1.0;
  return work->a[thread_id] * work->b[thread_id];
}

Then, an external driver program doesn't need to worry about thread safety, and can do things like:

#pragma omp parallel for
for (x = 0.0; x < 1.0; x += 0.1)
  {
    double y = lib_calc_something(x, work);
    ...
  }

Option 2:

In this case, I would shift the thread-safety to the external driver program:

typedef struct
{
  double a;
  double b;
} lib_workspace;

double lib_calc_something(double x, lib_workspace * work)
{ 
  work->a = x - 2.0;
  work->b = x + 1.0;
  return work->a * work->b;
}

External driver program:

/* allocate MAX_THREADS copies of lib_workspace */
for (i = 0; i < MAX_THREADS; ++i)
  work[i] = lib_alloc();

#pragma omp parallel for
for (x = 0.0; x < 1.0; x += 0.1)
  {
    int thread_id = omp_get_thread_num();
    double y = lib_calc_something(x, work[thread_id]);
    ...
  }

Just wondering what the best practice is? It seems to me Option 1 is best, since I might want many external programs calling my library and not have to implement thread-safety in each external program. Any advice/wisdom is appreciated.

1

Option 1 can have problems with cache thrashing, where cache lines get continually flushed and reloaded by threads because they all write to the same cache line.

It looks like maybe you need an Option 3, using an array-of-structures rather than a structure-of-arrays. Similar to Option 1, but keeping all the workspace variables grouped together to reduce cache thrashing. Declare your struct like you have it in Option 2, but use an array of them.

typedef struct
{
  double a;
  double b;
} lib_workspace[MAX_THREADS];

Then make appropriate changes to your Option 1 code. This will also allow you to write code similar to how you have it in Option 2, if you have several accesses to workspace variables.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.