假设2个内核正在尝试将不同的值写入相同的RAM地址(1个字节),在同一时刻(正负eta),并且不使用任何互锁指令或内存屏障。在这种情况下会发生什么以及将什么值写入主RAM?第一个获胜?最后一个获胜?未确定的行为?
答案 0 :(得分:8)
x86(与所有其他主流SMP CPU架构一样)具有 coherent data caches 。两个差异缓存(例如,2个不同核心的L1D)不可能为同一缓存行保存冲突数据。
硬件强制执行订单(通过某些特定于实现的机制来断开关联,以防两个所有权请求从不同的核心到达同一时钟周期)。在大多数现代的x86 CPU中,第一个商店不会被写入RAM,因为它有一个共享的回写L3缓存来吸收一致性流量,而无需往返内存。
在全局订单中的两个商店之后出现的加载将看到由第二个商店存储的值。
(我假设我们正在谈论正常(非NT)存储到可缓存的内存区域(WB,而不是USWC,UC,甚至WT)。基本思想在任何一种情况下都是相同的但是,一个商店将首先出现,下一个商店会踩到它。如果在全局订单中发生了加载,那么第一个商店的数据可以暂时被观察到,但是否则数据从硬件选择做第二的商店将是长期的影响。
我们正在谈论单个字节,因此商店不能分成两个缓存行,因此每个地址都是自然对齐的,因此Why is integer assignment on a naturally aligned variable atomic on x86?中的所有内容都适用。
通过要求核心在可以修改之前获取对该缓存行的独占访问权来维护一致性(即通过从商店提交商店使其全局可见)队列到L1D缓存)。
这"获得独家访问"东西是使用the MESI protocol的变体完成的。高速缓存中的任何给定行都可以是修改(脏),独占(尚未写入),共享(干净副本;其他高速缓存也可能有副本,因此在写入之前需要RFO(读取/请求所有权)),或者无效。 MESIF(英特尔)/ MOESI(AMD)增加了额外的状态来优化协议,但不要改变基本逻辑,即任何时候只有一个核心可以改变一条线路。
如果我们关心对两条不同线路进行多次更改的排序,那么内存排序的记忆就会起作用。但对于这个关于"哪个存储获胜"这个问题,这一切都不重要。当商店在同一时钟周期内执行或退出时。
当商店执行时,它会进入商店队列。它可以提交到L1D并在退出之后随时变得全局可见,但不是之前;未退出的指令被视为推测性的,因此它们的架构效果必须在CPU核心之外不可见。投机载荷没有建筑效应,只有微架构 1 。
因此,如果两个商店都准备好在同一时间提交#34; (时钟不一定在核心之间同步),一个或另一个将首先获得RFO并获得独占访问,并使其存储数据全局可见。然后,不久之后,其他核心的RFO将成功并使用其数据更新缓存行,因此其商店在所有其他核心观察到的全局商店订单中排名第二。
x86具有总存储顺序内存模型,其中所有内核都遵守相同的顺序,即使存储到不同的缓存行(除了始终按程序顺序查看自己的存储)。像PowerPC这样的弱有序体系结构会允许某些内核看到与其他内核不同的总顺序,但这种重新排序只能在不同行的存储之间进行。单个缓存行始终有一个修改顺序。 (相对于彼此和其他存储的负载重新排序意味着您必须小心如何在弱有序的ISA上观察事物,但是MESI强加的缓存行有一个修改顺序)。 / p>
哪一个赢得比赛可能取决于环形总线上核心布局相对于线路映射到哪个共享L3缓存片段的平淡无奇的东西。 (注意使用单词" race":这是种族条件和错误描述的种类。在两个不同步的商店中编写代码并不总是错误的更新相同的位置,你不关心哪一个获胜,但这种情况很少见。)
BTW,现代x86 CPU在多个内核争用原子读 - 修改 - 写入同一缓存线(因而是holding onto it for multiple clock cycles to makelock add byte [rdi], 1
atomic)时具有硬件仲裁,但是常规加载/存储只需要拥有单个循环的高速缓存行,用于执行加载或提交存储。我认为lock
ed指令的仲裁是一个不同的东西,当多个核心试图将存储提交到同一个缓存行时,核心会从中获胜。除非您使用pause
指令,否则内核会假设其他内核不会修改相同的高速缓存行,并且推测性地提前加载,因此如果确实发生了内存排序错误推测。 (What are the latency and throughput costs of producer-consumer sharing of a memory location between hyper-siblings versus non-hyper siblings?)
IDK,如果两个线程都只是在没有加载的情况下进行存储时发生了类似的事情,但可能不是因为存储器没有被推测性地重新排序并且与存储队列的无序执行分离。一旦商店指令退休,商店肯定会发生,所以OoO exec不必等待它实际提交。 (实际上它已经从OoO核心退出,然后才能提交,因为这就是CPU如何知道它是非推测性的;即没有早期指令出现故障或者是一个错误预测的分支)
脚注:
答案 1 :(得分:3)
他们将最终被排序,可能在L1缓存之间。一次写入将首先出现,另一次写入将成为第二次。无论哪一个是第二个将是后续读取将会看到的结果。