Friday, February 20, 2009

Supercomputing Course: OpenMP Directives

Last time, we talked about OpenMP syntax and some directives. Next we'll talk about a few more.

The sections directive is useful if you have a few separate parallelizeable tasks that you want different threads to run. For maximum portability and efficiency, I don't recommend this directive, for reasons that you will see. But if you have a couple of different tasks to parallelize, here's the syntax you would use:

#pragma omp sections
{
    #pragma omp section
    /* first section */
    #pragma omp section
    /* second section */
}
As you can see, in the example above, if you had more than two threads, only two of them would be used, and the rest would be idle. If this is the only way you can parallelize the work, then it's better than nothing, but if there's another angle to attack the problem, then I'd try that.

In certain algorithms, you may need thread synchronization. It could be that you have a task that depends on a previous task being completed, so you need to make all the threads wait until the first task completes before going on to the second. Or, you may have a task that's not thread safe, so you need to have only one thread at a time perform that task. Or you may have a task that only one thread can perform. If so, you are in luck, because there are synchronization directives just for these purposes!

The barrier directive creates a stopping point which a thread reaches and cannot proceed past until all the threads have reached the barrier. The threads don't all have to reach the exact same barrier call in order for them to stop there, but they do have to encounter the same sequence of work-sharing and barrier regions. The syntax is wonderfully simple:

#pragma omp barrier

The critical directive specifies a section of code which must be executed by only one thread at a time. Give each critical section its own unique name; otherwise all identically named critical sections considered the same critical section.

#pragma omp critical [name]
{
// code
}

A single section is useful for parts of the code that must be executed by only one thread, e.g., writing out to a file.

#pragma omp single
{
// code
}

There are many other directives, but I'll just cover one last one: reduce. The reduce directive is used to reduce a list of variables into one, using an operator such as sum, product, etc. In C, valid operators are +, -, *, &, ^, |, &&, or || . Here is an example:

#pragma parallel for reduction(+:n_eq)
for (i = 0; i < N; i++) {
   if (a[i] == b[i]) {
     n_eq = n_eq+1;
   }
}

Next installment: Variable scope

No comments: