Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
In this article, we have explained the concept of #
pragma omp critical with a C++ code example, when it is used and the difference between omp critical and omp single and atomic.
Table of contents:
- pragma omp critical
- When to use pragma omp critical?
- pragma omp critical vs omp single
- pragma omp critical vs omp atomic
Pre-requisite:
- Understand how to use OpenMP to parallelize C++ code from the basics.
pragma omp critical
pragma omp critical is an OpenMP directive that is used to prevent race conditions and force threads to execute a specific code segment one by one.
Following is the syntax to use pragma omp critical:
#pragma omp critical
{
// code snippet
}
The code snippet enclosed within this omp direction (critical) will be:
- Executed by one thread at a time
- So, if there are N threads, each thread will execute this code snippet. While a thread I is executing this code snippet, other threads will have to wait before another thread will start executing the code snippet.
Following is a C++ code example:
#include <iostream>
int main() {
// shared variable
int sum_shared = 0;
#pragma omp parallel
{
// private variable
int sum_local = 0;
#pragma omp for nowait
for (int i = 0; i < 10; ++i) {
sum_local += i;
}
// critical section as we update
// shared variable/ data
#pragma omp critical
{
sum_shared += sum_local;
}
}
std::cout << sum_shared << std::endl;
return 0;
}
Set OMP_NUM_THREADS and run it to check the output.
export OMP_NUM_THREADS=8
The output will vary depending on number of threads. Note we have wrapped the shared variable in omp critical so for a given number of threads, it will be same or else it would have differed.
When to use pragma omp critical?
One should use pragma omp critical when:
- a particular code segment involve data that depends on its previous state or uses shared data among multiple threads. For example, this code line:
data += data + computed;
This code line adds the value "computed" to the previous value of data and the value of data is updated. If multiple threads try to execute this code line at once, both threads will get the previous value of data and value written by one thread will become permanent. This nullifies the computation done by the other thread.
To avoid such situations omp critical is used:
#pragma omp critical
{
data += data + computed;
}
One popular use case is the 3 loop approach of Convolution where the part when the output of dot product is written in the output matrix is defined as omp critical section.
- Avoid any potential data race conditions.
pragma omp critical vs omp single
The difference between pragma omp critical vs omp single is covered in this summary table:
pragma omp critical vs omp single | ||
---|---|---|
Point | omp critical | omp single |
Meaning | Run code segment one by one by all threads | Run code segment once by any thread |
Number of times code is executed | Number of threads | 1 |
Use case | Avoid race condition | Manage control variables or signals |
Following code snippet illustrates the difference between the two:
int a=0, b=0;
#pragma omp parallel num_threads(16)
{
#pragma omp single
a++;
#pragma omp critical
b++;
}
std::cout << "a=" << a << ", b=" << b << std::endl;
The output will be
a=1, b=16
pragma omp critical vs omp atomic
#pragma omp atomic
pragma omp atomic is used to define a memory location or object as critical while "pragma omp critical" is used to define an entire code segment as critical.
In practice, pragma omp critical is more used compared to pragma omp atomic.
With this article at OpenGenus, you must have the complete idea of how to use pragma omp critical in your C++ code.