use*_*390 1 c++ atomic cpu-architecture stdatomic
考虑两个线程,T1 和 T2,它们分别存储和加载一个原子整数 a_i。让我们进一步假设,这家店在执行前正在执行的负荷启动。以前,我的意思是绝对的时间意义。
T1 T2
// other_instructions here... // ...
a_i.store(7, memory_order_relaxed) // other instructions here
// other instructions here // ...
a_i.load(memory_order_relaxed)
// other instructions here
Run Code Online (Sandbox Code Playgroud)
是否保证T2在加载后看到值7?
是否保证T2在加载后看到值7?
内存顺序在这里无关紧要;原子操作是原子的。只要您确保写入“发生在”读取之前(您在问题的前提下声明为真),并且没有其他干预操作,T2 将读取由 T1 写入的值。这是原子操作的本质,内存顺序不会修改这一点。
什么订单存储器控制是如果T2看到7(无论是“之前发生”确保与否),它是否能访问其它由T1修改的数据之前它存储7进入原子。对于relaxed内存排序,T2 没有这样的保证。
注意:您将问题从负载“发生在” store 之后,当 store与 load 明确“同步”时的情况更改为更加模糊的情况。就 C++ 对象模型而言,没有“绝对时间”。特定原子对象上的所有原子操作都按顺序发生,但除非有某些东西在两次加载之间明确创建“发生在/之后”关系,否则无法知道加载了什么值。这将是两种可能性之一,但无法知道哪一种。
(我正在回答更新的问题;Nicol 回答了原始问题,该问题在 C++“发生之前”术语中指定了“之后”,包括同步,这意味着读者可以保证看到作者所做的事情。并不是说他们是以锁步循环运行;C++ 没有任何“循环”的概念。)
我正在回答 C++ 如何在普通的现代 CPU 上运行。当然,ISO C++ 没有提及 CPU 架构,只是在atomic<>C++ 标准中关于一致性保证目的的注释中提到普通硬件具有一致性缓存。
我所说的之前,是指绝对时间意义上的之前。
如果您的意思是存储在加载执行之前变得全局可见,那么根据定义加载将看到它。但如果您指的是正常计算机体系结构意义上的“执行”,那么不,没有任何保证。如果存储在不同的内核上同时运行,则需要一些时间才能对其他线程可见。
现代 CPU使用存储缓冲区将存储执行与其他核心的可见性分离,因此执行可以是推测性的和无序执行,而不会导致核心外部可见混乱,因此执行不必因缓存未命中而停止商店。缓存是一致的;您无法从中读取“陈旧”值,但商店需要一些时间才能对其他核心可见。(在计算机体系结构术语中,存储通过将数据+地址写入存储缓冲区来“执行”。当它从存储缓冲区提交到 L1d 缓存时,在已知它是非推测性的之后,它变得全局可见。)
核心需要先获得缓存行的独占所有权,然后才能对其进行修改(MESI 独占或修改状态),因此,如果它在需要提交时尚未拥有该行,则会发送 RFO(读取所有权)从存储缓冲区到 L1d 缓存的存储。在核心看到该 RFO 之前,它可以继续让负载读取该行(即“执行”负载 - 请注意,在高性能 CPU 中,负载和存储本质上是不同的,核心希望尽早加载数据,但这样做商店晚了)。
相关:如果线程 1 也执行了一些稍后的加载,即使在保持其他所有内容有序的强有序 CPU 上,存储缓冲区也是您获得 StoreLoad 重新排序的方式。或者在具有强有序内存模型(例如 x86)的 CPU 上,该模型维持了除存储缓冲区之外的所有事情都按程序顺序发生的错觉。
内存屏障只是命令该核心的操作。彼此之间,例如,完整的屏障会阻止稍后加载执行,直到执行了较早的存储+加载,并且存储缓冲区已耗尽到屏障点,因此它仅包含稍后的加载(如果有的话)。
障碍对于另一个核心是否看到商店没有影响,除非另一个核心已经看到其他商店的先决条件。然后,通过屏障(或等效地发布/获取),您可以保证另一个核心也将看到发布存储之前的所有其他内容。
Jeff Preshing 的内存操作心智模型(作为访问远程服务器的源代码控制操作)是一个有用的模型:您可以相对于彼此对自己的操作进行排序,但是来自不同内核的管道中的请求可以以不同的方式到达服务器(共享内存)。命令。
这就是为什么 C++ 只将可见性指定为“最终”/“立即”,如果您已经看到(通过获取加载)来自发布存储的值,则可以保证看到较早的内容。(“及时”的含义取决于硬件。在现代多核系统上通常低于 100 ns(具体取决于您要测量的内容),尽管多插槽可能会更慢。如果 我不使用围栏,需要多长时间是否需要一个核心才能看到另一个核心的写入?)
看到存储本身(release、seq_cst,或者如果您不需要同步其他加载/存储,甚至可以放松)无论发生还是不发生,这就是在线程之间创建之前/之后概念的原因。由于 CPU 只能通过共享内存(或处理器间中断)看到彼此的操作,因此没有很多好的方法来建立任何同时性的概念。非常像物理学中的相对论,如果两件事没有发生在同一个地方,就很难说它们同时发生:这取决于观察者,因为能够看到任一事件的延迟。
(在诸如现代 x86 之类的机器上,TSC在内核之间同步(这在单插槽多核系统中尤其常见,显然也是大多数(?)多插槽主板),您实际上可以找到绝对时间戳来建立哪个核心在何时执行什么,但无序执行仍然是一个很大的混杂因素。流水线 CPU 很难准确说出任何给定指令的“执行”时间。而且由于通过内存进行的通信不是零延迟,因此也不是零延迟尝试以这种方式建立同时性通常很有用。)
| 归档时间: |
|
| 查看次数: |
134 次 |
| 最近记录: |