使用编译器内在函数实现自旋锁以同步 OpenMP 线程
Posted
技术标签:
【中文标题】使用编译器内在函数实现自旋锁以同步 OpenMP 线程【英文标题】:Implementing spinlocks to synchronize OpenMP threads using compiler intrinsics 【发布时间】:2016-06-23 14:29:56 【问题描述】:请看以下代码 sn-p 使用 OpenMP 进行并行化:
char lock = 0;
#pragma omp parallel
while(!__sync_bool_compare_and_swap(&lock, 0, 1));
printf("Thread: %d is working!\n", omp_get_thread_num());
sleep(1);
printf("Thread: %d unlocks lock!\n", omp_get_thread_num());
lock = 0;
是否有可能线程同时锁定锁,即使锁定是原子的 __sync_bool_compare_and_swap? 例如,并非所有线程都具有一致的内存视图?
【问题讨论】:
__sync_*()
是一个完整的障碍,lock = 0;
不是。
你为什么要这样做? OpenMP 正是为了提供适当的抽象而设计的,例如#pragma omp crtical
在你的情况下。从description of __sync_bool_compare_and_swap
开始,我强烈假设内存屏障用于强制执行一致的内存视图。但正如 EOF 所注意到的,lock = 0;
可能由于重新排序而成为问题。
虽然我完全同意你的看法,但我目前正在使用我尚未编写的代码,并且在这段代码中,同步得到了加强,就像我在代码中呈现它的方式一样。并且 Intel Inspector 会检测竞争条件,如果同步按我预期的那样工作,这应该是不可能的。
可以想象英特尔 Inspector 会产生误报吗?
【参考方案1】:
如果您坚持不以 OpenMP 方式执行此操作:
您肯定需要一个编译器屏障来防止lock=0
成为reordered at compile time。
如果您只针对 x86,则只需一个编译器障碍即可;否则在lock=0
之前使用C11 atomic_thread_fence(memory_order_release)
,这只是x86 上的编译器屏障,但会在弱排序架构上发出必要的指令。
或者将 lock
设为原子类型,并使用 C11 标准原子发布存储将其设置为 0。
在lock cmpxchg
上旋转以获取锁定是非常低效的。您应该在负载上旋转直到锁可用,然后尝试使用它。例如你应该写一些可以编译成代码的东西like this minimal but real asm spinlock implementation.
例如使用带有memory_order_acquire
的C11 原子负载。然后使用普通的xchg
,而不是cmpxchg
,并检查您是否获得了锁,或者其他线程是否在您之前获得了锁。
【讨论】:
以上是关于使用编译器内在函数实现自旋锁以同步 OpenMP 线程的主要内容,如果未能解决你的问题,请参考以下文章
在 Xcode 4 (LLVM GCC) 中使用 _mm_shuffle_ps 时可能出现 OpenMP + SSE 错误