OpenMP操作数组时omp atomic性能远低于omp critical问题咨询
核心结论
你观测到的性能差异和操作对象是数组无关,核心原因是你写的两种测试的锁粒度完全不对等,测试场景不具备可比性,你的atomic使用逻辑没问题,但对比测试的写法存在错误。
1. 测试代码的逻辑差异
看你代码里的编译分支逻辑:
- 定义
OMP_CRITICAL时,#pragma omp critical是加在整个for循环的外层:相当于每个线程只需要抢1次锁,抢到之后就独占CPU跑完自己的全部1000万次循环,全程没有其他锁开销,本质上所有线程的计算过程是串行执行的,几乎没有重复的锁竞争。 - 定义
OMP_ATOMIC时,#pragma omp atomic update是加在循环内部的每次+=操作前:相当于每次数组更新都要执行一次硬件原子指令,全程要做1000万次原子操作,再加上线程更新的数组元素存在重叠,会触发大量缓存行一致性同步(缓存行颠簸),总开销自然远高于只抢1次锁的critical版本。
2. 公平对比的测试方法
你只需要把critical的pragma移动到和atomic完全相同的位置,也就是循环内部、a[xxx] += i语句的前方,再编译测试,就会得到和标量场景一致的、atomic性能优于critical的结果:
// 修改后公平对比的代码片段 #pragma omp parallel { int const thread_idx = omp_get_thread_num(); int i; for (i = 0; i < N_EACH; i++) { #ifdef OMP_CRITICAL #pragma omp critical #endif /* OMP_CRITICAL */ #ifdef OMP_ATOMIC #pragma omp atomic update #endif /* OMP_ATOMIC */ a[thread_idx * (N_EACH - N_OVERLAP) + i] += i; } }
修改后你会观察到critical版本的耗时会远高于现在的测试结果,且慢于atomic版本。
3. 数组重叠场景的性能补充说明
如果你实际业务场景必须给每次更新单独加保护、且数组元素重叠率很高,确实可能出现atomic性能不如粗粒度critical的情况:
原子操作的优势是细粒度、无操作系统层面的锁调度开销,但是当大量原子操作集中在同一个/同一批缓存行时,硬件缓存一致性协议的同步开销会急剧上升;如果粗粒度critical加锁后单次能执行的操作足够多,锁竞争的开销被摊薄,反而性能会更高,这是场景特性导致的,和操作对象是数组还是标量没有直接关系。
内容的提问来源于stack exchange,提问作者user3708067
相关产品推荐
相关产品推荐

