42

对于任何std::atomic<T>T 是原始类型的地方:

如果我盲目地使用std::memory_order_acq_relfor fetch_xxxoperations 和std::memory_order_acquirefor loadoperation 和std::memory_order_releasefor storeoperation (我的意思是就像重置这些函数的默认内存顺序一样)

  • 结果是否与我std::memory_order_seq_cst对任何声明的操作使用(默认使用)相同?
  • 如果结果相同,那么std::memory_order_seq_cst在效率方面,这种用法与使用有何不同?
4

2 回答 2

85

原子操作的 C++11 内存排序参数指定了排序约束。如果您使用 进行存储std::memory_order_release,并且来自另一个线程的加载读取该值,则来自第二个线程的后续读取操作将看到第一个线程在存储释放之前或之后std::memory_order_acquire存储到任何内存位置的任何值存储到任何这些内存位置

如果存储和后续加载都是,std::memory_order_seq_cst那么这两个线程之间的关系是相同的。您需要更多线程才能看到差异。

例如std::atomic<int>变量xy,最初都是 0。

线程 1:

x.store(1,std::memory_order_release);

线程 2:

y.store(1,std::memory_order_release);

线程 3:

int a=x.load(std::memory_order_acquire); // x before y
int b=y.load(std::memory_order_acquire); 

线程 4:

int c=y.load(std::memory_order_acquire); // y before x
int d=x.load(std::memory_order_acquire);

x正如所写,与和的存储之间没有关系y,因此很可能在线程 3 和线程4 中看到a==1,和。b==0c==1d==0

如果所有内存排序都更改为,std::memory_order_seq_cst则这将强制存储之间的排序为xy。因此,如果线程 3 看到a==1然后b==0那意味着 store tox必须在 store to 之前y,所以如果线程 4 看到c==1,意味着 store toy已经完成,那么 store tox也必须完成,所以我们必须有d==1

实际上,std::memory_order_seq_cst根据您的编译器和处理器体系结构,在任何地方使用都会增加加载或存储或两者的额外开销。例如,x86 处理器的一种常用技术是使用XCHG指令而不是存储MOV指令std::memory_order_seq_cst,以提供必要的排序保证,而对于std::memory_order_release普通的MOV就足够了。在具有更宽松内存架构的系统上,开销可能更大,因为普通加载和存储的保证更少。

内存排序很难。在我的书中,我几乎用了整整一章来讲述它。

于 2013-02-13T22:29:40.070 回答
12

内存排序可能非常棘手,而且弄错的影响通常非常微妙。

所有内存排序的关键点是它保证“已经发生”,而不是将发生什么。例如,如果您将某些内容存储到几个变量中(例如x = 7; y = 11;),那么另一个处理器可能能够在看到x 中y的值之前看到 11 。通过在设置和设置7之间使用内存排序操作,您正在使用的处理器将保证在继续存储内容之前已将其写入内存。xyx = 7;y

大多数情况下,写入发生的顺序并不重要,只要值最终更新即可。但是,如果我们有一个带有整数的循环缓冲区,我们会执行以下操作:

buffer[index] = 32;
index = (index + 1)  % buffersize; 

并且其他一些线程正在使用index来确定新值已被写入,那么我们需要先32写入,然后再index更新。否则,其他线程可能会获取old数据。

这同样适用于使信号量、互斥锁和类似的东西起作用——这就是为什么术语释放和获取用于内存屏障类型的原因。

现在,这cst是最严格的排序规则 - 它强制在处理器可以继续执行更多操作之前,您已写入的数据的读取和写入都进入内存。这将比执行特定的获取或释放障碍要慢。它强制处理器确保存储和加载已经完成,而不是仅仅存储或加载。

这有多大区别?它高度依赖于系统架构是什么。在某些系统上,缓存需要 [部分地] 刷新,并从一个内核发送中断到另一个内核以说“请在继续之前执行此缓存刷新工作” - 这可能需要数百个周期。在其他处理器上,它只比执行常规内存写入慢一些。X86 非常擅长做到这一点。例如,某些类型的嵌入式处理器(某些型号的 - 不确定?)ARM 需要在处理器中进行更多工作以确保一切正常。

于 2013-02-13T20:07:07.403 回答