3.
OpenMP Programming Model
Introduction to OpenMP
• OpenMP is an API for writing multithreaded applications in a shared memory
environment
• It consists of a set of compiler directives and library routines
• Relatively easy to create multi-threaded applications in Fortran, C and C++
• Standardises the last 15 or so years of SMP development and practice
• Currently supported by
– Hardware vendors
▪ Intel, HP, SGI, Sun, IBM
– Software tools vendors
▪ Intel, KAI, PGI, PSR, APR, Absoft
– Application vendors
▪ ANSYS, Fluent, Oxford Molecular, NAG, DOE ASCI, Dash, Livermore Software, ...
• Support is common and growing
Ümit Demirbaga (PhD)
The OpenMP Programming Model
• A master thread spawns teams of threads as needed
• Parallelism is added incrementally; the serial program evolves into a parallel program
Master thread
Parallel regions
Ümit Demirbaga (PhD)
The OpenMP Programming Model
• Programmer inserts OpenMP directives (Fortran comments, C #pragmas) at key locations
in the source code.
• Compiler interprets these directives and generates library calls to parallelise code regions.
Serial: Parallel:
void main(){ void main(){
double x[1000]; double x[1000];
for (int i=0; i<1000; i++){ #pragma omp parallel for
big_calc(x[i]); for (int i=0; i<1000; i++){
} big_calc(x[i]);
} }
}
Split up loop iterations among a team of threads
Ümit Demirbaga (PhD)
The OpenMP Programming Model
• Number of threads can be controlled from within the program, or using the
environment variable OMP_NUM_THREADS.
• The programmer is responsible for managing synchronisation and data dependencies!
• Compiling on OSC systems:
Intel Compiler – IA64 Cluster Intel Compiler – IA32 Cluster
efc -openmp prog.f ifc -openmp prog.f
efc -openmp prog.f90 ifc -openmp prog.f90
ecc -openmp prog.c icc -openmp prog.c
Ümit Demirbaga (PhD)
How do Threads Interact?
• Shared memory model
– Threads communicate by sharing variables.
• Unintended sharing of data can lead to “race conditions”
– When the program’s outcome changes as the threads are scheduled differently.
• To control race conditions, use synchronization to avoid data conflicts
• Synchronisation is expensive!
– Think about changing how data is organised to minimise the need for synchronisation.
Ümit Demirbaga (PhD)
The Basics of OpenMP
• General syntax rules
• The parallel region
• Execution modes
• OpenMP directive clauses
• Work-sharing constructs
• Combined parallel work-sharing constructs
• Environment variables
• Runtime environment routines
• Interlude: data dependencies
Ümit Demirbaga (PhD)
General Syntax Rules
• Most OpenMP constructs are compiler directives or C pragmas
– For C and C++, pragmas take the form
#pragma omp construct [clause
[clause]...]
– For Fortran, directives take one of the forms:
c$omp construct [clause [clause]...]
!$omp construct [clause [clause]...]
*$omp construct [clause [clause]...]
• Since these are directives, compilers that don’t support OpenMP can still compile
OpenMP programs (serially, of course!)
Ümit Demirbaga (PhD)
General Syntax Rules
• Most OpenMP directives apply to structured blocks.
– A block of code with one entry point at the top, and one exit point at the bottom.
The only branches allowed are STOP statements in Fortran and exit() in
C/C++
c$omp parallel
c$omp parallel
10 wrk(id) = junk(id)
10 wrk(id) = junk(id) 30 res(id) = wrk(id)**2
res(id) = wrk(id)**2 if (conv(res)) goto 20
if (conv(res)) goto 10 goto 10
c$omp end parallel c$omp end parallel
print *, id if (not_done) goto 30
20 print *, id
A structured block
Not a structured block!
Ümit Demirbaga (PhD)
The Parallel Region
• C/C++ syntax:
#pragma omp parallel \
private (var1, var2, …) \
shared (var1, var2, …) \
firstprivate(var1, var2, …) \
copyin(var1, var2, …) \
reduction(operator:var1, var2, …) \
if(expression) \
default(shared|none) \
{
…a structured block of code…
}
Ümit Demirbaga (PhD)
The Parallel Region
• The number of threads created upon entering the parallel region is controlled by the value
of the environment variable OMP_NUM_THREADS
– Can also be controlled by a function call from within the program.
• Each thread executes the block of code enclosed in the parallel region
• In general, there is no synchronization between threads in the parallel region!
– Different threads reach particular statements at unpredictable times.
• When all threads reach the end of the parallel region, all but the master thread go out of
existence, and the master continues on alone.
Ümit Demirbaga (PhD)
The Parallel Region
• Each thread has a thread number, which is an integer from 0 (the master thread) to the
number of threads minus one.
– Can be determined by a call to omp_get_thread_num()
• Threads can execute different paths of statements in the parallel region
– Typically achieved by branching on the thread number:
#pragma omp parallel
{
myid = omp_get_thread_num();
if (myid == 0)
do_something();
else
do_something_else(myid);
}
Ümit Demirbaga (PhD)
Parallel Regions: Execution Modes
• “Dynamic mode” (the default)
– The number of threads used in a parallel region can vary, under control of the
operating system, from one parallel region to the next.
– Setting the number of threads just sets the maximum number of threads; you might get
fewer!
• “Static mode”
– The number of threads is fixed by the programmer; you must always get this many (or
else fail to run).
• Parallel regions may be nested, but a compiler may choose to “serialize” the inner parallel
region, i.e., run it on a single thread.
• Execution mode is controlled by
– The environment variable OMP_DYNAMIC
– The OMP function omp_set_dynamic()
Ümit Demirbaga (PhD)
OpenMP Directive Clauses
• shared(var1,var2,…)
– Variables to be shared among all threads (threads access same memory locations).
• private(var1,var2,…)
– Each thread has its own copy of the variables for the duration of the parallel code.
• firstprivate(var1,var2,…)
– Private variables that are initialized when parallel code is entered.
• lastprivate(var1,var2,…)
– Private variables that save their values at the last (serial) iteration.
• if(expression)
– Only parallelize if expressionis true.
• default(shared|private|none)
– Specifies default scoping for variables in parallel code.
• schedule(type [,chunk])
– Controls how loop iterations are distributed among threads.
• reduction(operator|intrinsic:var1,var2…)
– Ensures that a reduction operation (e.g., a global sum) is performed safely.
Ümit Demirbaga (PhD)