Assembly 在重新排序时,CPU实际上是在另一条指令之前执行指令的,还是只有最终结果才给出这一点;幻觉;?
根据我读到的内容,CPU可以对指令的执行重新排序,而内存屏障可以防止指令从内存屏障之前到之后以及从内存屏障之后到之前的重新排序 但有一点我不确定。假设我有以下说明:Assembly 在重新排序时,CPU实际上是在另一条指令之前执行指令的,还是只有最终结果才给出这一点;幻觉;?,assembly,x86,memory-barriers,Assembly,X86,Memory Barriers,根据我读到的内容,CPU可以对指令的执行重新排序,而内存屏障可以防止指令从内存屏障之前到之后以及从内存屏障之后到之前的重新排序 但有一点我不确定。假设我有以下说明: store x store y store x mfence store y 假设CPU决定在存储x之前执行存储y CPU是如何做到这一点的,它是否完全忽略存储x并首先执行存储y?还是会发生以下情况 存储x已执行,但未立即完成(变为 待定) 执行存储y,并立即完成 挂起的存储x已完成 因此,基本上,这给了一种“错觉”,即指令是按
store x
store y
store x
mfence
store y
假设CPU决定在存储x
之前执行存储y
CPU是如何做到这一点的,它是否完全忽略存储x
并首先执行存储y
?还是会发生以下情况
存储x
已执行,但未立即完成(变为
待定)存储x
已完成我问这个问题是为了了解记忆障碍是如何工作的 例如,假设我有以下说明:
store x
store y
store x
mfence
store y
现在,当CPU执行这些指令时,会发生以下情况吗
存储x
已执行,但未立即完成(变为
待定)mfence
现在执行,因为此指令是内存
屏障,CPU将确保所有挂起的操作
它(存储x
)将在继续执行指令之前完成mfence
不能防止无序执行它仅确保
mfence
之前的所有内存加载和存储在mfence
之后执行任何内存加载或存储之前都已序列化
见:
对在MFENCE指令之前发出的所有“从内存加载”和“存储到内存”指令执行序列化操作。此序列化操作可确保在MFENCE指令之后的任何加载或存储指令全局可见之前,按程序顺序在MFENCE指令之前的每个加载和存储指令全局可见
X86在任何情况下都受到OoO内存访问的限制x86体系结构确实已经内置了一些内存排序规则。
其要点是,内存访问很少接受重新排序 以下是英特尔的官方评论: 索引中列出了最有用的要点:-) 回写(WB)内存的内存顺序
*加载不会与其他加载一起重新排序,存储也不会与其他存储一起重新排序
*存储不会使用旧的加载重新排序
*可以使用旧商店将货物重新排序到不同的位置
[…]
*加载和存储不会使用锁重新排序 回到您的问题 重新排序时,CPU是否在另一条指令之前执行指令
是的,您可以在计时代码时看到这一点 让我给你举个例子,假设我们有一个AMD捷豹,它可以并行执行2条指令,并且具有完整的OoO
a: mov ebx,[eax] //1 cycle throughput
b: mov ecx,2 //pairs
c: imul eax,edx //3 cycles latency
d: add eax,ebp //1 cycle, needs to wait for c
通常,此代码段需要1+3+1=5个周期。
但是,CPU将按以下顺序执行此操作:
c: imul eax,edx //3 cycle latency
a: mov ebx,[eax'] //pairs, eax is renamed to eax' in the register rename buffer
b: mov ecx,2 //1 cycle
d: add eax,ebp //1 cycle waits for c
这只需要4个周期。3代表a,1代表d,其余的全部交错。显然,在c和d之间存在压缩更多指令的空间,如果CPU有任何适用的指令,它就会这样做 请注意,CPU会重新排序一个内存负载,只要它与另一个内存负载无关(以及一些其他限制,请参见上文)。
还要注意的是,AMD和Intel遵循完全相同的语义 在超级标量处理器上,您可以让操作排队等待前面的指令完成。想象一下这样的代码:
...
div %esi # divide edx:eax by esi
mov %eax,(%ebx) # store quotient in (%ebx)
mov $1,(%ecx) # store 1 in (%ecx)
在超级标量处理器上,第一条
mov
指令将在调度div
指令之后立即遇到。然而,当时div还没有完成。因此,存储指令将在指令队列中排队,直到div%esi
的结果在%eax
中可用。在下一个周期中,处理器遇到mov$1,(%ecx)
。由于立即$1
立即可用,处理器无需等待,可以立即执行存储。在调度存储之后的一段时间,div
指令完成,导致存储从指令队列中释放并执行
这就是为什么存储的顺序与机器代码指定的顺序不同。CPU有额外的逻辑,以确保程序员通常看不到这些细节,但根据您为之编程的体系结构,可能存在不同的工件。无序的唯一原因是实际执行无序,而错觉是它们按顺序执行。请注意,有一些规则,其中之一是:对内存的写入不会与其他写入一起重新排序(假设WB和没有显式非时态之类的花哨内容),所有外围设备都需要在写入之前进行设置,如go/enable/run等,无序写入将是一场灾难;d=e+f;h=5;g=a+d。d=可能发生在a=之前,一切都会好起来,或者可能移动h=5。想一想,如果某个寄存器很忙,而另一个寄存器不忙,则可以在不改变程序功能的情况下切入,然后运行该寄存器。不知道x86,但在arm上,在让其他任何内存操作失效之前,您可以使用内存屏障来刷新写缓冲区或使缓存失效