NUMA上的mov + mfence安全吗?

qbo*_*lec 4 c++ x86 memory-model numa stdatomic

我看到g ++生成一个简单的movfor x.load()mov+ mfencefor x.store(y).考虑这个经典的例子:

#include<atomic>
#include<thread>
std::atomic<bool> x,y;
bool r1;
bool r2;
void go1(){
    x.store(true);
}
void go2(){
    y.store(true);
}
bool go3(){
    bool a=x.load();
    bool b=y.load();
    r1 = a && !b;
}
bool go4(){
    bool b=y.load();
    bool a=x.load();
    r2= b && !a;
}





int main() {
    std::thread t1(go1);
    std::thread t2(go2);
    std::thread t3(go3);
    std::thread t4(go4);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    return r1*2 + r2;
}
Run Code Online (Sandbox Code Playgroud)

其中根据https://godbolt.org/z/APS4ZY go1和go2被翻译成

go1():
        mov     BYTE PTR x[rip], 1
        mfence
        ret
go2():
        mov     BYTE PTR y[rip], 1
        mfence
        ret
Run Code Online (Sandbox Code Playgroud)

对于这个例子,我问是否有可能线程t3和t4不同意t1和t2完成写入的顺序"逐滴"到它们各自的内存视图.特别考虑NUMA架构,其中t3恰好"靠近"t1并且t4"更接近"t2.是否会发生t1或t2的存储缓冲区"即使在达到之前过早冲洗" mfence,然后t3或t4有可能比计划更快地观察到写入?

Pet*_*des 6

是的,这很安全.您需要为NUMA安全代码启用特殊的编译器选项,因为asm不需要不同.

NUMA甚至与此无关; 多核单插槽x86系统已经可以像x86内存模型那样进行尽可能多的内存重新排序.(可能不太经常或时间窗口较小.)


TLDR.1:你似乎误解了什么mfence.它是运行它的核心的本地屏障(包括StoreLoad,唯一的重新排序x86允许没有非NT加载/存储的障碍).这与此完全无关,即使x86的排序很弱: 我们正在寻找来自不同内核的1个商店,因此订购单个内核的操作.彼此无所谓.

(mfence只是让核心等待任何负载,直到它的存储全局可见.当存储提交mfence等待它时,没有什么特别的事情发生. 内存屏障是否确保缓存一致性已经完成?)


TL:DR.2:其他线程是否会以相同的顺序看到对不同线程中不同位置的两次原子写入?C++允许不同的线程在商店订单上与轻松或释放存储不同(当然要获取负载以排除LoadLoad重新排序),但不能seq_cst.

在可能的架构上,编译器需要在seq-cst存储上有额外的障碍来防止它. 在x86这是不是可能,句号. 任何允许这种重新排序的类似x86的系统实际上都不 x86,并且无法正确运行所有x86软件.

你可以买到所有主流的x86系统真正的x86,与相干高速缓存和服从的x86内存模型.


x86的TSO内存模型要求所有内核都能就总商店订单达成一致

因此,相关规则实际上是内存模型的名称.

TSO属性直接来自每个核心,使其自己的存储保持私有,直到它们承诺L1d,并且具有连贯的缓存.

存储缓冲区意味着核心在全局可见之前始终会看到自己的存储,除非它mfence在重新加载之前使用StoreLoad屏障.

数据在核心之间获取的唯一方法是通过提交L1d缓存使其全局可见; 在其他人之前不与某些核心共享.(无论NUMA如何,这对TSO都是必不可少的).

其余的内存排序规则主要是关于内核中的内部重新排序:它确保其存储按程序顺序从存储缓冲区提交到L1d,并且在任何先前的加载已经读取它们的值之后.(以及确保LoadLoad排序的其他内部规则,包括内存顺序错误推测管道刷新,如果加载顺序推测读取我们在"允许"读取值之前丢失缓存行的值.)

当核心具有处于"已修改"状态的相关行时,数据只能从存储缓冲区提交到专用L1d,这意味着每个其他核心都将其置于"无效"状态.这(以及其他MESI规则)保持一致性:不同高速缓存中的高速缓存行不会有冲突的副本. 因此,一旦商店提交缓存,其他任何核心都无法加载陈旧值.(线程之间的数据交换将用于在一个Core上执行HT?)

一个常见的误解是,在其他CPU停止加载陈旧值之前,商店必须通过系统渗透.在使用MESI维护连贯缓存的普通系统中,这是100%错误. 当你谈到t3"更接近"t1时,你似乎也遭受了这种误解. 如果你有非连贯的DMA,那么DMA设备可能就是这样,正是因为那些DMA读取与参与MESI协议的CPU共享的内存视图不一致.(但现代x86也有缓存一致的DMA.)

事实上,违反TSO需要一些非常时髦的行为,其中一个商店在变得对所有人可见之前对其他核心可见. PowerPC在现实生活中为同一物理核心上的逻辑线程执行此操作,窥探彼此尚未提交到L1d高速缓存的已退役存储.请参阅我的答案是否 会在其他线程中以相同的顺序看到对不同线程中不同位置的两个原子写入? 即使是在有缺陷的国际航空运输协会(ISP)上,它也很少见.


使用x86 CPU但具有非连贯共享内存的系统是(或将是)非常不同的野兽

(我不确定是否存在这样的野兽.)

这更像是紧密耦合的超级计算机集群而不是单一机器.如果你正在考虑这个问题,那不仅仅是NUMA,它根本不同,你不能在不同的一致性域中运行普通的多线程软件.

正如维基百科所说,基本上所有NUMA系统都是缓存一致的NUMA,即ccNUMA.

虽然设计和构建更简单,但在标准的冯·诺依曼架构编程模型中,非缓存一致的NUMA系统变得非常复杂.

任何使用x86 CPU的非连贯共享内存系统都不会在不同的一致性域中运行单个内核实例.它可能有一个自定义MPI库和/或其他自定义库,以使用共享内存和显式刷新/一致性来在一致性域(系统)之间共享数据.

您可以从单个进程启动的任何线程肯定会共享缓存一致的内存视图,并遵守x86内存模型,否则您的系统会崩溃/有硬件错误.(我不知道存在任何此类硬件错误,需要在真实硬件中解决.)

具有一个或多个Xeon Phi PCIe卡的系统将每个Xeon Phi加速器视为单独的"系统",因为它们与主存储器或彼此不相干,仅在内部连贯.请参阅@ Hadi关于数据缓存如何在此示例中路由对象的答案的底部部分.你可能会将一些工作卸载到Xeon Phi加速器上,类似于你将工作卸载到GPU上的方式,但这可以通过消息传递等方式完成.你会不会对主SKYLAKE微架构(例如)运行某些线程的CPU上,而在至强融核KNL内核上运行同一进程的其他普通线程.如果Xeon Phi卡运行的是操作系统,它将是Linux的一个单独实例,或者与主机系统上运行的实例无关.


x86 NUMA系统通过在从本地DRAM加载之前侦听其他套接字来实现MESI,以维持缓存一致性.

当然,RFO(读取所有权)请求被广播到其他套接字.

新一代Xeon已经引入了越来越多的snoop设置来权衡性能的不同方面.(例如,更积极的窥探需要在套接字之间的链路上节省更多带宽,但可以减少套接字之间的核心间延迟.)

可在四路和更大系统(E7 v1..4)中工作的芯片具有探听滤波器; 双插槽E5 v1..4只是使用我读过的QPI带宽的一小部分广播窥探到另一个插槽.(这适用于前Skylake-X Xeon,Broadwell和更早版本.SKX在芯片上使用网状网络,并且可能总是在套接字之间进行某种窥探过滤.我不确定它是做什么的.BDW和之前使用过的包容性L3缓存作为本地核心的监听过滤器,但SKX具有非包含L3,因此即使在单个套接字内也需要其他东西进行监听过滤.

AMD多插座芯片曾经使用过Hypertransport.Zen在一个插槽内的4个核心集群之间使用Infinity Fabric; 我假设它也在套接字之间使用它.

(有趣的事实:多插槽的AMD K10 Opteron处理器的HyperTransport可以创建在8字节边界撕裂,而单个插座内的16字节SIMD加载/存储在实践原子. SSE指令:哪些CPU可以做原子16B内存操作?x86上的原子性.如果你把它算作重新排序,那就是多插槽可以比单插槽做更多内存怪异的情况.但这与NUMA本身无关;你对所有内存附加的内存都是一样的用于UMA设置的插座.)


有关:

另请参阅重复链接LOCK XCHG和MOV + MFENCE之间的逻辑和性能有何不同?对于xchg与mov + mfence.在现代CPU上,尤其是Skylake,对某些测试方式而言,mov+ mfence肯定比较慢xchg,而且两者都是做seq_cst商店的等效方式.

A releaserelaxed商店只需要一个普通的mov,并且仍然具有相同的TSO订购保证.

我认为,即使是微弱有序的NT商店仍然可以通过他们可以达成一致的顺序看到所有内核."弱点"是全球可见的顺序.来自核心的其他负载+商店.


归档时间:

查看次数:

149 次

最近记录:

6 年,6 月 前