ARM允许重新排序加载后续存储,以便以下伪代码:
// CPU 0 | // CPU 1
temp0 = x; | temp1 = y;
y = 1; | x = 1;
可以导致temp0 == temp1 == 1
(并且,这在实践中也是可观察到的).我无法理解这是怎么发生的; 似乎有序提交会阻止它(这是我的理解,它存在于几乎所有的OOO处理器中).我的理由是"在提交之前,负载必须具有其值,它在存储之前提交,并且在提交之前,存储的值不会对其他处理器可见."
我猜我的一个假设肯定是错的,并且必须遵循下列之一:
说明不需要提交一路有序.稍后的存储可以安全地提交并在之前的加载之前变得可见,只要在存储提交核心时可以保证先前的加载(以及所有中间指令)不会触发异常,并且加载的地址是保证与商店不同.
负载可以在其值已知之前提交.我不知道如何实现这一点.
商店在提交之前可以显示.也许某个内存缓冲区允许将存储转发到另一个线程的加载,即使负载先前已加入?
还有别的吗?
有许多假设的微体系结构特征可以解释这种行为,但我最好的是那些实际存在于现代弱有序CPU中的那些.
vTune文档将Memory Order Machine Clear性能事件描述为:
当来自另一个处理器的侦听请求与管道中数据操作的源匹配时,将发生内存排序(MO)机器清除。在这种情况下,在撤消正在进行的装载和存储之前,应清理管道。
但是我不明白为什么会这样。在不同逻辑处理器上的加载和存储之间没有同步顺序。
处理器可以假装在所有当前的机上数据操作都提交后进行监听。
此问题也在此处描述
每当CPU内核检测到“内存排序冲突”时,就会触发内存排序机清除。基本上,这意味着一些当前待处理的指令试图访问我们刚刚发现同时写入了其他CPU内核的内存。由于这些指令仍被标记为待处理,而“此存储器刚刚被写入”事件则表示其他某个内核已成功完成写入,因此,待处理指令以及取决于结果的所有内容都是错误的:当我们开始执行这些指令时在说明中,我们使用的内存内容版本已过时。因此,我们需要把所有工作都扔掉,然后再做完。这很清楚。
但这对我来说没有任何意义,CPU不需要重新执行Load-Queue中的装载,因为没有针对非锁定装载/存储的总订单。
我可以看到一个问题,即允许对负载进行重新排序:
;foo is 0
mov eax, [foo] ;inst 1
mov ebx, [foo] ;inst 2
mov ecx, [foo] ;inst 3
Run Code Online (Sandbox Code Playgroud)
如果执行顺序为1 3 2,则mov [foo], 1
3至2之间的存储会导致
eax = 0
ebx = 1
ecx = 0
Run Code Online (Sandbox Code Playgroud)
这确实违反了内存排序规则。
但是负载不能随负载重新排序,那么当来自另一个内核的监听请求与任何飞行负载的来源相匹配时,为什么英特尔的CPU会刷新管道?
此行为可以防止什么错误情况?
我对指令流水线有一些疑问。
我有一个大会
0x111: div %ecx
0x112: add $0x13,%ecx #The address 112 is not real, I just kept to show that there is no instructions between div and add
Run Code Online (Sandbox Code Playgroud)
程序计数器,RIP 指向0x111
. 我的疑问是,将处理器执行(指令流水线或顺序变化/不限原因)0x112's
指令而RIP
处于0x111
。如果我在 RIP 停止执行0x111
,是否有任何机会0x112
被执行并且%ecx
价值是0X13
?
由于其 TSO 内存模型,X86 保证所有商店的总顺序。我的问题是是否有人知道这是如何实际实施的。
我对所有 4 个围栏是如何实现的印象很好,所以我可以解释如何保留本地秩序。但是 4 个栅栏只会给 PO;它不会给您 TSO(我知道 TSO 允许旧商店跳到新负载前面,因此只需要 4 个围栏中的 3 个)。
单个地址上所有内存操作的总顺序是一致性的责任。但我想知道英特尔(特别是 Skylake)如何在多个地址的商店上实现总订单。
x86 intel cpu-architecture memory-barriers micro-architecture