我试图理解什么是内存屏障。
根据我目前所知,内存屏障(例如:mfence
)用于防止指令在内存屏障之前或之后重新排序。
以下是内存屏障的一个示例:
instruction 1
instruction 2
instruction 3
mfence
instruction 4
instruction 5
instruction 6
现在我的问题是: mfence
指令仅仅是告诉CPU执行指令的顺序的标记吗?还是它像执行其他指令(例如:mov
)一样被CPU实际执行的指令呢?
我试图理解什么是内存屏障。
根据我目前所知,内存屏障(例如:mfence
)用于防止指令在内存屏障之前或之后重新排序。
以下是内存屏障的一个示例:
instruction 1
instruction 2
instruction 3
mfence
instruction 4
instruction 5
instruction 6
现在我的问题是: mfence
指令仅仅是告诉CPU执行指令的顺序的标记吗?还是它像执行其他指令(例如:mov
)一样被CPU实际执行的指令呢?
在CPU遇到的代码中,每个字节序列都是CPU执行的指令,没有其他类型的指令。
您可以在英特尔指令集参考和mfence的特定页面中清楚地看到这一点。
正如您从摘录中所看到的那样,MFENCE
将在MFENCE指令之前发出的所有从内存读取和写入内存的指令进行序列化操作。此序列化操作确保在任何MFENCE指令之后的任何加载或存储指令之前,在程序顺序中在MFENCE指令之前的所有加载和存储指令成为全局可见。MFENCE指令与所有加载和存储指令、其他MFENCE指令、任何LFENCE和SFENCE指令以及任何序列化指令(例如CPUID指令)有序。MFENCE不序列化指令流。 弱排序的内存类型可用于通过诸如乱序发射、推测性读取、写组合和写折叠等技术实现更高的处理器性能。消费者识别或知道数据是弱排序的程度因应用程序而异,并且可能对此数据的生产者不知情。MFENCE指令提供了一种性能高效的方式,可确保在生成弱有序结果的例程和消耗该数据的例程之间进行加载和存储排序。
处理器可以自由地从使用WB、WC和WT内存类型的系统内存区域中推测性地获取和缓存数据。这种推测性获取可以在任何时候发生,不与指令执行绑定。因此,它与MFENCE指令的执行没有顺序关系;数据可以在执行MFENCE指令之前、期间或之后被推测性地带入缓存。
MFence
指令会执行相当多的工作,而不仅仅是一种标记。mfence
对流水线流程的影响。以Skylake流水线为例。考虑以下指令序列:inst1
store1
inst2
load1
inst3
mfence
inst4
store2
load2
inst5
mfence
uop时,它需要确保在所有上游内存uop变得全局可见之前(这意味着存储已退役并且加载至少已完成),不会执行任何下游内存uop。这适用于所有内存访问,无论所访问的区域的内存类型如何。可以通过使调度器不向存储或加载缓冲区发出任何下游存储或加载uop直到缓冲区被排空,或通过发出下游存储或加载uop并标记它们以便与缓冲区中的所有现有内存uop区分开来来实现此目的。栅栏上方或下方的所有非内存uop仍然可以无序执行。在本例中,一旦store1
退役并且load1
完成(通过接收数据并将其保存在某个内部寄存器中),则认为mfence
指令已完成执行。我认为mfence
可能会占用后端(ROB或RS)中的任何资源,并且它可能会转换为多个uop。mfence
的工作原理。由于这是一个非常古老的专利,实现可能已经改变或者在不同的处理器中可能有所不同。在此我将对该专利进行总结。 mfence
被解码成三个μops。不幸的是,目前还不清楚这些μops具体用于什么。然后从保留站分配条目来保存μops,同时从加载和存储缓冲区中分配条目。这意味着加载缓冲区可以容纳真正的加载请求或栅栏(基本上是虚假的加载请求)的条目。同样,存储缓冲区可以容纳真正的存储请求和栅栏的条目。只有在所有先前的加载或存储μops(在各自的缓冲区中)已被退役后,mfence
μop才会被调度。当发生这种情况时,mfence
μop本身将作为内存请求发送到L1高速缓存控制器。控制器检查是否所有先前的请求都已完成。如果是,则它将被视为NOP,并且μop将从缓冲区中释放。否则,高速缓存控制器将拒绝mfence
μop。mfence
解码为4个微操作(融合和未融合),这些微操作在p2/p3和p4(AGU和store-data端口)上运行。至少在文本上,mfence
不必停止后续存储器的执行(并将存储数据放入存储器缓冲区),它只需停止这些存储器在栅栏之后成为全局可见(提交到L1d缓存)。然而,mfence
确实需要一种机制来阻止在所有先前的加载/存储器全部变为全局可见之前,后续加载的执行。如果英特尔的实现更加严格,那么这只是一种设计选择。 - Peter Cordesmemory-fences
和memory-barriers
标签不应该是同义词吗? - Hadi Braismfence
uops与Agner Fog的表格更新:他可能在SKL079的微码修复之前进行了测试,这使得mfence
变得更加昂贵。请参见Are loads and stores the only instructions that gets reordered?底部,我最近更新了该内容,因为我偶然发现了mfence
如此昂贵的原因解释。也许实现强制停顿一切解决方案的uops不同,但以前只有4个未融合域uops来阻止内存重排序。 - Peter Cordesmemory-order
标签应该是关于 ISA 内存模型的,而 memory-model
标签则是关于语言内存模型的。但似乎 memory-order
已经成为了 memory-barriers
的同义词,但实际上不应该是这样的。memory-barriers
标签只应该是 memory-fences
的同义词。memory-order
标签可以是 memory-ordering
的同义词。 - Hadi Braismfence是一条指令。
在Linux上使用它:
1/ 编写一个名为mfence.c的文件。
#include <stdio.h>
int main(){
printf("Disass me\n");
asm volatile ("mfence" ::: "memory");
return 0;
}
2/ 编译
gcc mfence.c mfence
3/ 反汇编
objdump -d mfence | grep -A 10 "<main>:"
000000000000063a <main>:
63a: 55 push %rbp
63b: 48 89 e5 mov %rsp,%rbp
63e: 48 8d 3d 9f 00 00 00 lea 0x9f(%rip),%rdi # 6e4 <_IO_stdin_used+0x4>
645: e8 c6 fe ff ff callq 510 <puts@plt>
64a: 0f ae f0 mfence
64d: b8 00 00 00 00 mov $0x0,%eax
652: 5d pop %rbp
653: c3 retq
654: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
65b: 00 00 00
4/注意,在第64行a处,mfence
是一个(3位)指令(0f ae f0)。
因此,它是一种CPU指令(如mov
):处理器需要在到达该指令之前解码先前的指令,否则它无法猜测它的对齐方式。
例如,0f ae f0
可以出现在地址中,因此CPU无法将其用作标记。
最后,这只是一条老式指令,在其管道中的执行点上,在执行下一条指令之前,它将在管道中进一步同步内存访问。
注意:在Windows中,请使用宏_ReadWriteBarrier
来产生一个mfence。
std :: atomic_signal_fence()
或GNU Casm("" :::“memory”)
仅是源代码中的标记,并且编译为零条指令。它们存在于编译时阻止重新排序,并且在目标架构具有比源语言更强的内存模型时特别有用(例如C ++ - > x86汇编)。 http://preshing.com/20120625/memory-ordering-at-compile-time/ 了解更多信息。 - Peter Cordes