微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当带有指令的内存被另一个内核更改时,CPU 管道会发生什么变化?

如何解决当带有指令的内存被另一个内核更改时,CPU 管道会发生什么变化?

我试图了解 cpu 管道的“获取”阶段如何与内存交互。

假设我有这些说明:

4:  bb 01 00 00 00          mov    $1,%ebx
9:  bb 02 00 00 00          mov    $2,%ebx
e:  b3 03                   mov    $3,%bl

如果 cpu1 将 00 48 c7 c3 04 00 00 00 写入内存地址 8(即 64 位对齐)同时 cpu2 正在执行这些相同的指令,会发生什么?指令流会自动从 2 条指令变为 1 条指令,如下所示:

4:  bb 01 00 00 00          mov    $1,%ebx
9:  48 c7 c3 04 00 00 00    mov    $4,%rbx

由于 cpu1 正在写入 cpu2 正在读取的同一内存,因此存在争用。 写入会导致 cpu2 管道在刷新其 L1 缓存时停顿吗? 假设 cpu2 刚刚完成了 mov $2 的“fetch”pĥase,为了重新获取更新的内存,它会被丢弃吗?

另外还有将 2 条指令改为 1 条指令时的原子性问题。

我发现了这个quite old document 提到“指令提取单元在每个时钟周期从指令高速缓存存储器中提取一个 32 字节的高速缓存线” 我认为这可以解释为每条指令从 L1 获取缓存线的新副本,即使它们共享相同的缓存线。 但我不知道这是否/如何适用于现代 cpu

如果上述正确,则意味着在将 mov $2 提取到管道中后,下一次提取可能会在地址 e获取更新的值并尝试执行 00 00 ( add %al,(%rax)) 可能会失败。

但是如果 mov $2获取mov $3 带入“指令缓存”,它会 认为下一次提取只会从该缓存中获取指令(并返回 mov $3)而不重新查询 L1 是否有意义? 这将有效地使这 2 条指令的获取原子化,只要它们共享一个缓存线。

那是什么?基本上有太多的未知数,太多我只能推测,所以我真的很感激管道的 2 个获取阶段如何与它们访问的内存交互(变化)的逐个时钟周期细分。

解决方法

它因实现而异,但通常由多处理器的 cache coherency protocol 管理。简单来说,当 CPU1 写入内存位置时,该位置将在系统中的所有其他缓存中失效。因此,该写入将使 CPU2 的指令缓存中的行以及 CPU2 的 uop 缓存中的任何(部分)解码指令无效(如果它有这样的东西)。因此,当 CPU2 去获取/执行下一条指令时,所有这些缓存都会丢失,并且会在重新获取内容时停止。根据缓存一致性协议,这可能涉及等待写入到达内存,或者可能直接从 CPU1 的 dcache 中获取修改后的数据,或者可能通过某个共享缓存进行处理。

,

正如 Chris 所说,RFO(读取所有权)可以随时使 I-cache 行无效。

根据超标量提取组的排列方式,缓存线可以在 mov 处提取 5 字节 9: 之间无效,但在 e: 处提取下一条指令之前。

当获取最终发生时(该内核再次获取缓存行的共享副本),RIP = e,它将获取 mov $4,%rbx 的最后 2 个字节。 交叉修改代码需要确保没有其他内核在它要编写一条长指令的中间执行。

在这种情况下,您会得到 00 00 add %al,(%rax)

还要注意,写CPU需要确保修改是原子的,例如使用 8 字节存储(Intel P6 和更高版本的 CPU 保证在 1 个缓存行内的任何对齐处存储最多 8 个字节是原子的;AMD 没有),或 lock cmpxchglock cmpxchg16b。否则,读者可能会看到部分更新的说明。您可以将指令提取视为执行 16 字节原子加载或类似操作。


“指令获取单元在每个时钟周期从指令缓存中获取一个 32 字节的缓存线”,我认为这可以解释为每条指令从 L1 获取缓存线的新副本,

没有

然后将那个宽提取块解码成多个 x86 指令!宽取指的重点是一次拉入多条指令,而不是为每条指令分别重做。该文档似乎是关于 P6(Pentium III)的,尽管 P6 一次只执行 16 字节的实际提取,进入一个 32 字节宽的缓冲区,让 CPU 占用 16 字节的窗口。

P6 是 3-wide 超标量,每个时钟周期可以解码最多 16 字节的机器码,最多包含 3 条指令。 (但是有一个预解码阶段来首先找到指令长度......)

有关详细信息,请参阅 Agner Fog 的微架构指南 (https://agner.org/optimize/),(重点关注与转变软件性能相关的细节。)后来的微架构在预解码和解码之间添加队列。请参阅 Agner Fog 的微架构指南和 https://realworldtech.com/merom/(核心 2)的那些部分。

当然,请参阅 https://realworldtech.com/sandy-bridge 以了解带有 uop 缓存的更现代的 x86。也https://en.wikichip.org/wiki/amd/microarchitectures/zen_2#Core最近的 AMD。

在阅读其中任何一个之前要获得良好的背景知识,Modern Microprocessors: A 90-Minute Guide!


对于修改自己代码的核心,请参阅:Observing stale instruction fetching on x86 with self-modifying code - 这是不同的(并且更难),因为必须从较早与较晚指令的代码提取中整理出商店的乱序执行按程序顺序。即 store 必须变为可见的时刻是固定的,这与另一个核心不同,它在发生时才发生。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。