CPU 是否进行虚拟化内存位置的推测执行?

问题描述 投票:0回答:1

考虑在伪汇编中进行昂贵的计算后寄存器的经典重用:

r2 = cos(r1)
*(r3) = r2
r2 = r5 + r6
*(r4) = r2

为了能够充分使用算术单元,执行单元可能会这样做:

r2 = cos(r1)
*(r3) = r2

同时:

r2bis = r5 + r6
*(r4) = r2bis

其中 r2bis 是虚拟化(或重命名)的 r2 寄存器。

现在想象我们在一个寄存器较差的 CPU 中工作(或者我们有很多但它们已经被使用)并将数据放在某个临时堆栈位置中:

*(sp+C) = cos(r1)
*(r3) = *(sp+C)
*(sp+C) = r5 + r6
*(r4) = *(sp+C)

是否存在地址已知的内存位置(因为(sp+C)已经可以计算)被执行单元虚拟化以允许相同的两个执行并行进行?

这种情况可能看起来非常愚蠢,因为编译器的任务可能是在不太受限的堆栈空间上找到另一个位置(与非常受限的寄存器空间不同)。但其他情况可能并不那么愚蠢,因为虚拟化内存可能允许推测执行必须在内存中存储短期数据的条件分支。这对于没有简单方法将对象字段放入寄存器的语言尤其重要,例如 Java,除了最简单的情况之外:您必须排除“引用”(指针)转义以避免

new
动态分配和将 Java 类实例转换为 C++ 类自动实例(可以在堆栈中分配或在寄存器中)。 (甚至 C++ 也很难在简单平面类的明显简单使用中没有真正的
this
指针。)

cpu cpu-architecture virtualization cpu-registers speculative-execution
1个回答
0
投票

是的,通过存储缓冲区的存储到加载转发允许多个独立的存储/重新加载链在同一位置同时运行。与寄存器重命名一样,这允许独立存储执行,而不会在先前的存储上出现 WAW 或 WAR 危险,并加载到同一位置。

CPU 甚至可以推测加载是否依赖于先前的存储(如果地址尚未准备好用于所有先前的存储),如果它们猜测不会,则从 L1d 缓存获取数据而不是等待。这就是记忆消歧。如果他们猜错了,就需要回滚。


大多数有序CPU和所有乱序CPU都有存储缓冲区;有必要将推测执行与其他内核可见的缓存状态解耦:推测执行的 CPU 分支是否可以包含访问 RAM 的操作码?也是一种避免在缓存未命中存储上停滞的廉价方法。

快速路径存储转发(当重新加载与存储完全重叠时)在普通 CPU 上具有完整的吞吐量。 Intel CPU 上的存储转发停顿(当这种情况没有发生时)可以通过快速路径存储转发进行管道化,但不能相互进行管道化。据推测,它必须对存储缓冲区进行更详细的扫描,而不是只获取比加载更旧的第一个匹配条目,因为加载数据可能必须来自多个最近的存储,并且可能来自 L1d 缓存的一些字节。


一系列 x86 调用/ret 指令是否形成依赖链? 有来自

perf
的实验结果显示了这一点,其中几个
add qword [rsp], 0
指令进一步延长了依赖链。分支预测 + 推测执行意味着每个
call
启动一个新的依赖链,存储返回地址,而之前的
add
ret
加载/存储 + 加载仍在进行中。


零延迟内存重命名

某些 CPU 实际上可以通过与存储缓冲区分开匹配寻址模式来以零延迟“重命名”内存。 https://www.agner.org/forum/viewtopic.php?t=41 - Zen 2 和 Zen 4 有它。 https://chipsandcheese.com/2022/11/08/amds-zen-4-part-2-memory-subsystem-and-conclusion/ 说 Zen 3 也有,Alder Lake P 核也可以(金海湾)。 Ice Lake 似乎也是如此:

https://www.realworldtech.com/forum/?threadid=186393&curpostid=186393

跨边界传递小整数(例如虚拟函数)可以从中受益。或者堆栈参数(尤其是在 32 位代码中)。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.