I have the following piece of code (note: this is to help me understand the concept so it is an example code not something I intend to run)
List ml; //my_list
Element *e;
#pragma omp parallel
#pragma omp single
{
for(e=ml->first;e;e=e->next)
#pragma omp task
process(e);//random function
}
It was mentioned that e will always have the same value, I am trying to think why is that and what the value will be.
My try/reasoning:
The value of e is changing inside pragma omp single
, these changes don't get carried out inside task (If I am not mistaken no value of the single region get carried to inside the task unless I use something like firstprivate(e)
, moreover, the value it will take will be random since we didn't initialize e to any variable outside the single and parallel omp region and that what will e take as a value, if we had initialized outside to a value x for example then e will always be x
Any help correcting or verifying my reasoning would be appreciated.
I have added some comments to your example to hopefully make the behavior a bit clearer.
List ml; //my_list
Element *e;
#pragma omp parallel // e is shared for the parallel region
#pragma omp single
{
for(e=ml->first;e;e=e->next)
#pragma omp task // since e is shared, all tasks will see the "same" e
process(e);
}
What happens is this (indicated by the comments above): you're declaring e
outside of the scope of the parallel
constructs. As per the OpenMP specification, the variable will be shared across all the threads executing. The single
constructs then restricts execution to any one thread of the team (e
is still shared across all the threads, see https://www.openmp.org/spec-html/5.1/openmpsu113.html#x148-1600002.21.1).
When the picked thread encounters the task
construct, the OpenMP specification mandates that the created task from the task
inherits the sharing attributes of the e
variable (shared), so all created tasks will see the same variable and the picked thread may overwrite the e
variable while it executes the for
loop.
That's where the firstprivate(e)
comes in:
List ml; //my_list
Element *e;
#pragma omp parallel // e is shared for the parallel region
#pragma omp single
{
for(e=ml->first;e;e=e->next)
#pragma omp task firstprivate(e) // task now receives a private "copy" of e
process(e);
}
Here, the create tasks will have a private copy of e
that is initialized with the current value of e
as the picked thread progresses through the for
loop.
Another way to fix this would be this:
List ml; //my_list
#pragma omp parallel
#pragma omp single
{
Element *e; // e is thread-private inside the parallel region
for(e=ml->first;e;e=e->next)
#pragma omp task // task now receives a private "copy" of e w/o firstprivate
process(e);
}
Since in this example, the OpenMP specification mandates that the variable should be treated as if you specified firstprivate(e)
(see https://www.openmp.org/spec-html/5.1/openmpsu113.html#x148-1610002.21.1.1).