首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >内存屏障是CPU执行的指令,还是仅仅是一个标记?

内存屏障是CPU执行的指令,还是仅仅是一个标记?
EN

Stack Overflow用户
提问于 2017-03-10 17:20:21
回答 4查看 5.6K关注 0票数 22

我正在尝试理解记忆障碍到底是什么。根据我目前所知,内存屏障(例如:mfence)用于防止指令从内存屏障之前到之后以及从之后到之前的重新排序。

这是一个使用中的内存屏障的示例:

代码语言:javascript
复制
instruction 1
instruction 2
instruction 3
mfence
instruction 4
instruction 5
instruction 6

现在我的问题是:mfence指令只是一个标记,告诉CPU按什么顺序执行指令?或者它是CPU实际执行的指令,就像它执行其他指令一样(例如:mov)。

EN

回答 4

Stack Overflow用户

发布于 2017-03-11 03:43:29

CPU在其代码中遇到的每个字节序列都是CPU执行的指令。没有其他类型的指令。

MFENCE

对在MFENCE指令之前发出的所有从内存加载指令和存储到内存指令执行序列化操作。此序列化操作可确保在程序顺序中位于MFENCE指令之前的每个加载和存储指令在MFENCE指令之后的任何加载或存储指令之前全局可见。

MFENCE指令相对于所有加载与存储指令、其他MFENCE指令、任何LFENCE与SFENCE指令以及任何序列化指令(如CPUID指令)进行排序。弱有序内存类型可以通过无序发布、推测性读取、写组合和写折叠等技术来实现更高的处理器性能。数据消费者识别或知道数据是弱排序的程度因应用程序而异,并且可能不为该数据的生产者所知。MFENCE指令提供了一种高效的方法来确保产生弱排序结果的例程和使用该数据的例程之间的加载和存储顺序。

处理器可以自由地从使用WB、WC和WT内存类型的系统内存区域中推测性地获取和缓存数据。因此,它对于MFENCE指令的执行没有排序;数据可以在MFENCE指令执行之前、期间或之后推测性地放入缓存中。

正如您从摘录中看到的,MFence指令做了相当多的工作,而不仅仅是某种标记。

票数 24
EN

Stack Overflow用户

发布于 2018-05-12 05:08:17

Skylake管道为例。考虑以下指令序列:

代码语言:javascript
复制
inst1
store1
inst2
load1
inst3
mfence
inst4
store2
load2
inst5

但是,当调度器接收到mfence uop时,它需要确保在所有上游内存uop变得全局可见之前,不会执行mfence下游的内存uop(这意味着存储已退出且加载至少已完成)。这可以通过使调度器不向存储或加载缓冲器分别发出任何下游存储或加载微指令直到缓冲器被排空,或者通过发出下游存储或加载微指令并对它们进行标记以使得它们可以与缓冲器中的所有现有存储器微指令相区别来实现。围栏上方或下方的所有非内存uop仍然可以乱序执行。在本例中,一旦store1退出且load1完成(通过接收数据并将其保存在某个内部寄存器中),mfence指令就被视为已完成执行。我认为mfence可能会占用后端(ROB或RS)的任何资源,也可能不会占用任何资源,并且它可能会被转换为多个uop。

由于这是一个非常老的专利,实现可能已经改变,或者在不同的处理器上可能是不同的。我将在这里总结一下这个专利。mfence被解码成三个uop。然后,从保留站分配条目,分配条目以保持微操作,并且还从加载和存储缓冲器分配条目。这意味着加载缓冲区可以保存真实加载请求的条目,也可以保存栅栏(基本上是假加载请求)的条目。直到所有较早的加载或存储uop (在相应的缓冲区中)都已退役,才会分派mfence uop。当发生这种情况时,mfence uop本身将作为内存请求发送到L1缓存控制器。在这种情况下,它将被简单地视为NOP,并且uop将从缓冲区中去掉涂层。

票数 19
EN

Stack Overflow用户

发布于 2018-05-13 02:05:29

要在Linux上获得它,请执行以下操作:

1/写入mfence.c文件

代码语言:javascript
复制
#include <stdio.h>

int main(){
    printf("Disass me\n");
    asm volatile ("mfence" ::: "memory");
    return 0;
}

2/编译

gcc mfence.c mfence

objdump -d mfence | grep -A 10 "<main>:"

代码语言:javascript
复制
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/观察到64a行的mfence是(3位)指令(0f ae f0)

因此,这是一条cpu指令(如mov):处理器需要在到达它之前解码之前的指令,否则它无法猜测它是对齐的。

例如,0f ae f0可能出现在地址中,因此cpu不能将其用作标记。

最后,它只是一个旧的学校指令,在它在流水线中的执行点,它将同步流水线中进一步的内存访问,然后再执行下一条指令。

注意:在Windows上,使用中的宏_ReadWriteBarrier来生成macro

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42714599

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档