失败排序与 x86 原子操作相关吗?

问题描述 投票:0回答:1

考虑

compare_and_exchange_strong_explicit
的定义:

_Bool atomic_compare_exchange_strong_explicit( volatile A* obj,
                                               C* expected, C desired,
                                               memory_order succ,
                                               memory_order fail );

我对

memory_order fail
的用例感到困惑。考虑到 x86,很明显
lock cmpxchg
可能会失败,但也明确定义了(强调我的)

读取或写入无法使用 I/O 指令重新排序,锁定 说明,或序列化说明

这使得

memory_order fail
变得无关紧要,因为
lock
在任何情况下都能保证顺序一致性。

示例

#include <stdatomic.h>

void fail_seqcst(volatile int *p, int *expected, int *desirable){
    atomic_compare_exchange_strong_explicit(p, expected, desirable, memory_order_release, memory_order_seq_cst);
}

void fail_relaxed(volatile int *p, int *expected, int *desirable){
    atomic_compare_exchange_strong_explicit(p, expected, desirable, memory_order_release, memory_order_relaxed);
}

编译为

fail_relaxed:
        mov       ecx, edx                                      
        mov       eax, DWORD PTR [rsi]                          
        lock      
        cmpxchg   DWORD PTR [rdi], ecx                          
        mov       DWORD PTR [rsi], eax                          
        sete      al                                            
        movzx     eax, al                                       
        ret 

fail_seqcst:
        mov       ecx, edx                                      
        mov       eax, DWORD PTR [rsi]                          
        lock      
        cmpxchg   DWORD PTR [rdi], ecx                          
        mov       DWORD PTR [rsi], eax                          
        sete      al                                            
        movzx     eax, al                                       
        ret                                                     

神箭

编译器是否可以进行任何优化来区分

memory_order_relaxed
memory_order_seq_cst
在这种情况下的
x86
上的代码?或者也许有一种架构可以使这种差异变得显着?

c++ assembly x86 memory-barriers stdatomic
1个回答
0
投票

CAS_weak 和 CAS_strong 的

memory_order
的所有可能选择都会生成与
atomic_compare_exchanges_strong(ptr, &expected, desired, seq_cst)
相同的 x86 asm,即
lock cmpxchg

无论成功还是失败,

lock cmpxchg
都是像
atomic_thread_fence(seq_cst)
一样的完整屏障(与弱序ISA(例如AArch64)上的seq_cst加载、存储或RMW不同,其中SC RMW不会阻止稍后的宽松存储与较早的宽松存储重新排序,例如,后面的存储可以使用 RMW 的存储侧重新排序,而较早的存储可以使用 RMW 的负载侧重新排序)。操作与 ISO C++ 和某些真实 ISA 中的栅栏不同。因此它比 ISO C++ 要求的 seq_cst 操作强得多,就像完整的栅栏一样强。

和 x86

lock cmpxchg
甚至在失败时弄脏缓存行(再次与 LL/SC 实现不同,如果比较为假,它会分支存储尝试。)尽管 LL 也可能会尝试获取缓存行进入独占状态,因此即使避免了以后的写回,对于争用来说可能也好不了多少。我不确定这是如何运作的。

就 CPU 而言,

lock cmpxchg

 只是 ALU 和标志设置与 
lock add
xchg
 等始终成功的操作不同。 
这些都是完整的障碍,事实上,虚拟 lock or dword [rsp], 0
 或类似的东西是大多数编译器实现 
atomic_thread_fence(seq_cst)
 的方式,因为它比 
mfence
 更快。

英特尔甚至

记录它始终存储,因此处理器永远不会产生锁定读取而不产生锁定写入,尽管同一句话谈到“处理器总线的接口”。正常(可缓存)内存区域上的对齐操作仅获取缓存锁(延迟 MESI 响应),而不是 CPU 内核外部总线上实际可见的任何内容。因此,这可能不再是真正有意义的文档,至少是自 1995 年 P6(Pentium Pro)以来的一个好像的东西。

© www.soinside.com 2019 - 2024. All rights reserved.