【问题标题】:Does a memory barrier acts both as a marker and as an instruction?内存屏障是否既充当标记又充当指令?
【发布时间】:2018-10-24 13:58:18
【问题描述】:

我已经阅读了有关内存屏障如何工作的不同内容。

例如,用户 Johanthis question 中的回答说内存屏障是 CPU 执行的指令。

虽然用户 Peter Cordesthis question 中的评论说 CPU 如何重新排序指令:

它的读取速度比执行速度快,所以它可以看到一个窗口 即将到来的指示。有关详细信息,请参阅 x86 中的一些链接 标记 wiki,例如 Agner Fog 的 microarch pdf,以及 David Kanter 的 英特尔 Haswell 设计的文章。当然,如果你只是 谷歌搜索“乱序执行”,你会找到维基百科的文章, 您应该阅读。

所以我根据上面的评论猜测,如果指令之间存在内存屏障,CPU会看到这个内存屏障,导致CPU不会重新排序指令,所以这意味着内存屏障是供 CPU 看到而不是执行的“标记”。


现在我的猜测是,内存屏障既可以作为标记,也可以作为 CPU 执行的指令。

对于标记部分,CPU看到指令之间的内存屏障,导致CPU不重新排序指令。

至于指令部分,CPU会执行内存屏障指令,导致CPU做flush store buffer之类的事情,然后CPU会继续执行内存屏障之后的指令。

我说的对吗?

【问题讨论】:

  • 关于标签,assembly 标签只能用于有关汇编编程的问题或有关指令如何在 ISA 规范级别而不是实现级别工作的问题。类似地,cpu 标签应该只用于关于 CPU 利用率或配置或虚拟化等问题,而不是关于 CPU 内部的问题。您可以使用cpu-architecture 标签代替有关指令如何实现或 CPU 如何工作的问题,例如这个问题。
  • “标记”是什么意思?标记和指令之间有什么区别?在英特尔手册中,没有标记指令之类的东西。
  • 严格来说,根据定义,内存屏障只保证为某些内存操作提供排序,而不一定是指令。
  • @Hadi Brais “你所说的“标记”是什么意思?标记和指令到底有什么区别?在英特尔手册中,没有标记指令这样的东西." 我的意思是“标记”一条 CPU 只看到但不执行的指令(这是我编造的一个术语)。
  • @Steve 这个“标记”的定义没有多大意义。当然,刷新缓冲区是一种执行形式。我认为根本没有考虑“标记”一词。它没有以任何合理的方式映射到 CPU,也没有给我们任何新的见解。

标签: assembly x86 cpu-architecture instruction-set memory-barriers


【解决方案1】:

不,mfence 没有在指令流上序列化,lfence(也就是)不会刷新存储缓冲区。

(在 Skylake 的实践中,mfence 确实阻止了后续 ALU 指令的乱序执行,而不仅仅是加载。(Proof: experiment details at the bottom of this answer)。因此它被实现为执行屏障,即使在纸面上它不需要是一个。但lock xchg 不是,而且也是一个完整的障碍。)

我建议阅读 Jeff Preshing 的 Memory Barriers Are Like Source Control Operations 文章,以更好地了解内存屏障需要做什么以及它们不需要做什么。它们通常不会(不需要)阻止乱序执行。


内存屏障限制了内存操作可以全局可见的顺序,不是(必然)指令执行的顺序。阅读@BeeOnRope 对您的更新的回答再次上一个问题:Does an x86 CPU reorder instructions? 了解更多关于如何在没有 OoO exec 的情况下发生内存重新排序,以及如何在没有内存重新排序的情况下发生 OoO exec。

停止管道和刷新缓冲区是实现屏障的一种(低性能)方法,used on some ARM chips,但是具有更多内存排序跟踪的高性能 CPU 可以具有更便宜的内存屏障,只有限制内存操作的顺序,而不是所有指令。对于内存操作,它们控制对 L1d 缓存的访问顺序(在存储缓冲区的另一端),不一定是存储将数据写入存储缓冲区的顺序。

x86 已经需要大量内存顺序跟踪来进行正常加载/存储以实现高性能,同时保持其强排序内存模型,其中仅允许StoreLoad reordering 对内核外部的观察者可见(即存储可以被缓冲直到稍后加载后)。 (英特尔的优化手册使用术语“内存顺序缓冲区”或 MOB,而不是存储缓冲区,因为它还必须跟踪加载顺序。如果事实证明推测性加载也占用了数据,它必须执行内存排序机器清除早期。)现代 x86 CPU 保留了尊重内存模型的错觉,同时实际执行无序的加载和存储。

mfence 只需将标记写入内存顺序缓冲区即可完成其工作,而不会成为后面 ALU 指令乱序执行的障碍。此标记至少必须防止以后的加载执行,直到mfence 标记到达存储缓冲区的末尾。 (以及在弱排序 WC 内存上排序 NT 存储和操作)。

(但同样,更简单的行为是一种有效的实现选择,例如,在mfence 之后,不让任何存储将数据写入存储缓冲区,直到所有早期加载都已退出并且早期存储已提交到 L1d 缓存。即完全耗尽MOB / 存储缓冲区。我不确切知道当前的 Intel 或 AMD CPU 是做什么的。)


特别是在 Skylake 上,my testing shows mfence 是前端(融合域)的 4 个微指令,以及在执行端口上实际执行的 2 个微指令(一个用于端口 2/3(加载/存储地址),以及一个用于端口 4(存储数据))。大概它是一种特殊的uop,它将标记写入内存顺序缓冲区。不需要执行单元的 2 个微指令可能类似于 lfence。我不确定他们是否会阻止前端甚至发出稍后的加载,但希望不会因为这会阻止以后执行独立的 ALU 操作。


lfence 是一个有趣的案例:除了作为 LoadLoad + LoadStore 屏障(即使对于弱排序加载;正常加载/存储已经排序),lfence 也是一个弱 执行障碍(注意mfence不是,只是lfence)。在所有早期指令“在本地完成”之前,它无法执行。大概这意味着从无序的核心“退休”。

但是一个 store 不能提交到 L1d 缓存直到 之后 它无论如何都退休了(即在它被认为是非投机性的之后),所以等待 store 从 ROB 退休(ReOrder Buffer for uops) 与等待存储缓冲区清空不同。见Why is (or isn't?) SFENCE + LFENCE equivalent to MFENCE?

所以是的,CPU 管道确实必须在执行之前“通知”lfence,大概是在问题/重命名阶段。我的理解是lfence 在 ROB 为空之前无法发布。 (在 Intel CPU 上,lfence 是前端的 2 微指令,但根据 Agner Fog 的测试,它们都不需要执行单元。http://agner.org/optimize/。)

lfence 在 AMD Bulldozer 系列上更便宜:1 uop,每时钟 4 个吞吐量。 IIRC,它不会在这些 CPU 上进行部分序列化,因此您只能使用 lfence; rdtsc 阻止 rdtsc 在 Intel CPU 上提前采样时钟。


对于像cpuidiret 这样的完全序列化指令,它也会等到存储缓冲区耗尽。 (They're full memory barriers, as strong as mfence)。或类似的东西;它们是多个微指令,所以也许只有 last 一个进行序列化,我不确定cpuid 的实际工作发生在障碍的哪一侧(或者它是否不能与较早或较晚的说明)。无论如何,管道本身必须注意序列化指令,但完整的内存屏障效应可能来自执行 mfence 所做的微指令。


额外阅读:

在 AMD Bulldozer 系列上,sfencemfence 一样昂贵,并且可能是一个强大的障碍。 (x86 文档为每种障碍的强度设置了最小值;它们不会阻止它们变得更强大,因为这不是正确性问题)。 Ryzen 不同:sfence 每 20c 吞吐量一个,而mfence 每 70c 1 个。

sfence 在 Intel 上非常便宜(一个 uop 用于 port2/port3,一个 uop 用于 port4),并且只需订购 NT 商店 wrt。正常存储,不刷新存储缓冲区或序列化执行。它可以每 6 个周期执行一次。

sfence 在退休之前不会耗尽存储缓冲区。它本身不会成为全局可见的,直到所有前面的存储首先成为全局可见,但 这与存储缓冲区的执行管道。存储缓冲区总是试图耗尽自己(即提交存储到 L1d)所以sfence 不需要做任何特殊的事情,除了在 MOB 中放置一种特殊的标记来阻止 NT 存储重新排序过去,不像正规商店的标记,仅用于订购。常规存储和稍后加载。


它的读取速度比执行速度快,因此它可以看到一个显示即将执行的指令的窗口。

请参阅this answer I wrote,这是我评论的更详细版本。它介绍了现代 x86 CPU 如何通过查看尚未执行的指令来发现和利用指令级并行性的一些基础知识。

在具有高 ILP 的代码中,最近的 Intel CPU 实际上很容易在前端出现瓶颈;后端有如此多的执行单元,除非存在数据依赖性或缓存未命中,或者您使用大量只能在有限端口上运行的单条指令,否则它很少成为瓶颈。 (例如矢量洗牌)。但是任何时候后端跟不上前端,乱序窗口就会开始填充指令以找到并行性。

【讨论】:

  • 我看不懂内存屏障和mfence这两个粗体部分。据我了解,障碍mfencesfence 阻止年轻存储执行并耗尽存储缓冲区(这是英特尔所说的,并且必须做些什么才能使存储全局可见)。标记的作用是什么?会不会是对存储缓冲区的命令?
  • @MargaretBloom: sfence 在退休前不会耗尽存储缓冲区。在所有先前的存储首先成为全局可见之前,它不会成为全局可见的,但这通过存储缓冲区与执行管道分离。存储缓冲区总是试图耗尽自己(即提交存储到 L1d)所以sfence 不需要做任何特别的事情,除了在 MOB 中放置一种特殊的标记来停止 NT商店从重新订购过去,这与普通商店只订购 wrt 的标记不同。常规存储和稍后加载。
  • @MargaretBloom:更新了更多内容,有帮助吗?我是否需要更详细地解释存储缓冲区的“标记”是什么意思?加载和存储必须写入 MOB 以便它可以跟踪它们的顺序,并且 mfence + sfence 显然在 Intel CPU 上做了类似的事情,将某种标记写入 MOB。所以它有点像存储缓冲区的命令。
  • 非常感谢您的努力,但我认为答案对于提出的问题来说太复杂了。我认为只考虑一条指令作为示例(最简单的一条)就足够了。关于 AMD 处理器的内容以及不同围栏指令之间的差异以及关于序列化指令的内容使得在所提出问题的上下文中很难理解答案。更不用说答案中有九个关于各种相关内容的链接,其中包括更多指向更多内容的链接。这有点压倒性。
  • @HadiBrais:随意尝试更简单地回答这个问题。我可能需要更多时间来做一个简单的总结。但就像我说的那样,我不知道 OP 是在询问理论上干净/简单架构中的纯内存屏障,还是他们实际上是在询问真正 x86 CPU 上的 x86 屏障,这些问题都使这些问题变得复杂。我不确定如何比 BeeOnRope 对 OP 上一个问题的回答更清楚,IMO 清楚地解释了 OoO exec 与内存重新排序之间的区别。
猜你喜欢
  • 2015-10-15
  • 1970-01-01
  • 1970-01-01
  • 2017-07-31
  • 1970-01-01
  • 2018-10-29
  • 1970-01-01
  • 2017-09-08
  • 1970-01-01
相关资源
最近更新 更多