使用编译器内在函数实现自旋锁以同步 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 错误

使用x86_64汇编写一个自旋锁

RK3588平台开发系列讲解(同步与互斥篇)自旋锁介绍

《Linux内核设计与实现》读书笔记- 内核同步方法

程序并发操作中,解决数据同步的四种方法