在重新排序时,CPU是否实际上在另一个之前执行指令,或者只是最终结果给出了这个"错觉"?

时间:2017-03-11 01:00:44

标签: assembly x86 memory-barriers

根据我所读到的内容,CPU可以重新排序指令的执行,并且内存屏障可以防止在内存屏障之前和之后以及之后重新排序指令。

但有一点我不确定。说我有以下说明:

store x
store y

让我们说CPU决定在store y之前执行store x

CPU如何做到这一点,是否完全忽略store x并首先执行store y?或者是否会发生以下情况?:

  1. store x被执行,但没有立即完成(它变成了 申请中)。
  2. store y已执行,并立即完成。
  3. 待处理的store x已完成。
  4. 所以基本上,这给了"错觉"指令是按顺序执行的,即使它们没有执行,但它们只是无序完成。

    我问这个问题是为了理解记忆障碍是如何起作用的。

    例如说我有以下说明:

    store x
    mfence
    store y
    

    现在当CPU执行这些指令时,会发生以下情况吗?:

    1. store x被执行,但没有立即完成(它变成了 申请中)。
    2. 现在执行
    3. mfence,因为该指令是一个内存 屏障,CPU将确保之前的所有待处理操作 它(store x)将在继续执行指令之前完成。
    4. store y已执行。

2 个答案:

答案 0 :(得分:3)

mfence不会阻止无序执行 它只是确保在mfence之后执行任何内存加载或存储之前,mfence之前的所有内存加载和存储都被序列化。

请参阅:http://x86.renejeschke.de/html/file_module_x86_id_170.html

  

对MFENCE指令之前发出的所有内存加载和存储到内存指令执行序列化操作。此序列化操作保证在MFENCE指令之前的任何加载或存储指令全局可见之前,在程序顺序之前的每条加载和存储指令都是全局可见的。

X86在任何情况下都限制在OoO内存访问中
x86架构确实已经内置了一些内存排序规则 其中的要点是内存访问只能获得很少的重新排序。

以下是英特尔的官方文章:http://www.cs.cmu.edu/~410-f10/doc/Intel_Reordering_318147.pdf

该指数最有帮助地列出了要点: - )

  

回写(WB)内存的内存排序
  *负载不与其他负载重新排序,商店不与其他商店重新订购   *商店不会与较旧的货物重新订购   *负载可以与较旧的商店重新订购到不同的位置
  [...]
  *负载和存储不会使用锁重新排序

回到您的问题

  

在重新订购时,CPU是否实际执行了另一条指令   是的,您可以在为代码计时时看到这一点。

让我举个例子,让我们假设我们有一台可以并行执行2条指令且具有完整OoO的AMD美洲虎。

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遵循完全相同的语义。

答案 1 :(得分:0)

在超标量处理器上,您可以排队等待上一条指令完成的操作。想象一下这样的代码:

...
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有额外的逻辑来确保程序员通常不会看到这个细节,但是根据你编程的架构,可能存在不同的工件。