Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是mov+;在努马安全吗?_C++_X86_Memory Model_Numa_Stdatomic - Fatal编程技术网

C++ 是mov+;在努马安全吗?

C++ 是mov+;在努马安全吗?,c++,x86,memory-model,numa,stdatomic,C++,X86,Memory Model,Numa,Stdatomic,我看到g++为x.load()生成了一个简单的mov,为x.store(y)生成了mov+mfence。 考虑这个经典的例子: #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.lo

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

#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;
}
对于本例,我询问线程t3和t4是否可能对t1和t2执行的写入操作“向下”到各自的内存视图的顺序产生分歧。特别是考虑一个NUMA体系结构,其中T3恰好与T1“接近”,T4与T2“更接近”。
t1或t2的存储缓冲区是否会在到达
mfence
之前“提前刷新”,然后t3或t4有可能比计划的更早观察到写入?

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

NUMA甚至与此无关;多核单插槽x86系统已经可以执行x86内存模型所允许的内存重新排序。(可能不太频繁或时间窗口较小。)


TLDR.1:你似乎误解了mfence的功能。它是运行它的核心的本地屏障(包括StoreLoad,唯一的重新排序x86允许对非NT加载/存储没有屏障)。这与此完全无关,即使x86是弱排序的:我们正在查看来自不同内核的每个存储,因此单个内核操作的排序wrt。彼此都无所谓。

mfence
只会使该内核在其存储全局可见之前等待执行任何加载。当存储在
mfence
等待时提交时,不会发生任何特殊情况..)


<> TL:DR2: < Stime><强> C++允许不同线程在存储顺序上与松弛或释放存储区(不必加载负载以排除负载重排),而不是用<代码> SEQARCST 在可能的体系结构上,编译器需要在seq cst存储上设置额外的屏障来防止它在x86上这是不可能的,到此为止。任何允许这种重新排序的类似x86的系统实际上都不是x86,也无法正确运行所有x86软件

您可以购买的所有主流x86系统实际上都是x86,具有一致的缓存,并且遵循x86内存模型


x86要求所有核心都能就总存储订单达成一致 因此,相关的规则就是记忆模型的名字

  • -关于TSO与seq cst的一些简单内容。i、 e.完全订购+存储缓冲区
  • 尝试对x86内存模型进行形式化描述。它没有提到NUMA,因为它不相关
TSO属性直接从每个核心开始,在它们提交到L1d之前保持自己的存储的私有性,并从具有一致缓存开始。

存储缓冲区意味着核心总是在全局可见之前看到自己的存储,除非它在重新加载之前使用类似
mfence
的存储加载屏障

数据在核心之间传输的唯一方法是提交到L1d缓存,使其全局可见;不要先与某些内核共享,然后再与其他内核共享。(无论NUMA如何,这对TSO都是至关重要的)

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

只有当核心的相关行处于修改状态时,数据才能从存储缓冲区提交到专用L1d,这意味着其他每个核心的相关行都处于无效状态。这(以及其他MESI规则)保持了一致性:在不同的缓存中,缓存线的副本永远不会有冲突因此,一旦存储提交到缓存,其他核心就无法加载过时的值。()

一个常见的误解是,在其他CPU停止加载过时的值之前,存储必须通过系统渗透。在使用MESI维护一致缓存的正常系统中,这是100%错误的当您谈到t3“更接近”t1时,您似乎也有这种误解。如果您使用非相干DMA,则DMA设备可能会出现这种情况,因为这些DMA读取与参与MESI协议的CPU共享的内存视图不一致。(但现代x86也有缓存一致DMA。)

事实上,违反TSO需要一些非常时髦的行为,即存储在对所有内核可见之前对其他内核可见。PowerPC在现实生活中对同一物理内核上的逻辑线程执行此操作,这些线程窥探彼此的退役存储,这些存储尚未提交到L1d缓存。请看我的答案,它是罕见的,即使是弱序的ISA,允许它在纸上


使用x86 CPU但具有非一致共享内存的系统是(或将是)非常不同的 (我不确定是否有这样的野兽存在。)

这更像是紧密耦合的超级计算机集群,而不是单机。如果这就是你所想的,那不仅仅是NUMA,它是根本不同的,你不能跨不同的一致性域运行普通的多线程软件

,基本上所有NUMA系统都是缓存一致的NUMA,也称为ccNUMA。

尽管设计和构建更加简单,但非缓存一致NUMA系统在标准冯·诺依曼体系结构编程模型中编程变得异常复杂

任何使用x86 CPU的非一致性共享内存系统都不会跨不同的一致性域运行单个内核实例。它可能有一个自定义的MPI库a
go1():
        mov     BYTE PTR x[rip], 1
        mfence
        ret
go2():
        mov     BYTE PTR y[rip], 1
        mfence
        ret