views:

301

answers:

5

I have a large number of sets of integers, which I have, in turn, put into a vector of pointers. I need to be able to update these sets of integers in parallel without causing a race condition. More specifically. I am using OpenMP's "parallel for" construct.

For dealing with shared resources, OpenMP offers a handy "atomic directive," which allows one to avoid a race condition on a specific piece of memory without using locks. It would be convenient if I could use the "atomic directive" to prevent simultaneous updating to my integer sets, however, I'm not sure whether this is possible.

Basically, I want to know whether the following code could lead to a race condition

vector< set<int>* > membershipDirectory(numSets, new set<int>);

#pragma omp for schedule(guided,expandChunksize)
for(int i=0; i<100; i++)
  {
    set<int>* sp = membershipDirectory[rand()];
    #pragma omp atomic
      sp->insert(45);
  }

Note that I use a random integer for the index, because in my application, any thread might access any index (there is a random element in my larger application, but I need not go into details).

I have seen a similar example of this for incrementing an integer, but I'm not sure whether it works when working with a pointer to a container as in my case.

A: 

Your code is not clear. Assuming that membershipDirectory[5] is actually membershipDirectory[i], atomic directive is not needed. For two processors, for example, OpenMP produces two threads, one handles i = 0-49, another 50-99 intervals. In this case, there is no need to protect membershipDirectory[i]. atomic directive is required to protect some common resource which does not depend on the loop index, for example, total sum.

Alex Farber
Ok, I've cleared up the code using a random integer specify the index of the set to be updated. I did this to emphasize the fact than each thread might try to access the same set at the same time.
conradlee
It looks like this code does not meet formal atomic directive definition. I can think only about vector of critical sections/mutexes, with the same size as membershipDirectory. Insert statement should be protected by locking the mutex with the same index. In most cases, lock will be acquired immediately.
Alex Farber
+2  A: 

After searching around, I found the OpenMP C and C++ API manual on openmp.org, and in section 2.6.4, the limitations of the atomic construct are described.

Basically, the atomic directive can only be used with the following operators:

Unary: ++, -- (prefix and postfix)

Binary: +,-,*,/,^,&,|,<<,>>

So I will just use locks!

(In some situations critical sections might be preferable, but in my case locks will provide fine grained access to the shared resource, yielding better performance than a critical section.)

conradlee
use critical sections
aaa
Critical sections would also work, but they're a little crude. They easy to use, but one might be able to get better performance from good application of a set of locks. That is, one can have more finely grained control over a shared resources with many locks rather than one critical section (a critical section is like one big lock that blocks all threads).
conradlee
Global, or unnamed, critical sections may unnecessarily impact performance because every thread is effectively competing for the same global critical section. For that reason, OpenMP has named critical sections. Named critical sections allow for more fine-grained synchronization so only the threads that need to block on a particular section will blockhttp://software.intel.com/en-us/articles/more-work-sharing-with-openmp/
aaa
A: 

Blog post "Be careful when working with atomic directive".

Andrey Karpov
A: 

Another approach might be to use OpenMP 3's tasks. I write 'might be' because I'm not entirely sure what the intent of your program is. But have the feeling that you shouldn't have to use explicit locks for it.

High Performance Mark
A: 

you should not use atomic where expression is a function call, it only applies to simple expressions (with possibly built-ins: power, square root).

Instead use critical section (either named or default)

aaa