存储转发地址与数据:英特尔优化指南中的 STD 和 STA 有何区别?

Store forwarding Address vs Data: What the difference between STD and STA in the Intel Optimization guide?

我想知道是否有英特尔专家可以告诉我 STD 和 STA 在英特尔 Skylake 内核方面的区别。

在英特尔优化指南中,有一张图片描述了英特尔酷睿的"super-scalar ports"。

Here's the PDF。图片在第40页。

.

Here's another picture from page 78,这张图描述"Store Address"和"Store Data":

  1. 使用存储数据的地址准备存储转发和存储退役逻辑。

  2. 使用存储的数据准备存储转发和存储退役逻辑。

考虑到 Skylake 每个时钟周期可以执行 #1 3 次,但每个时钟周期只能执行 #2 一次,我很好奇这两者之间的区别。

在我看来 "natural" 存储转发将完成到数据的地址。但我不明白什么时候会完成数据的存储转发(又名:STD/端口 4)。有没有组装/优化专家可以帮助我准确理解 STD 和 STA 之间的区别?

已经几天没有回复了,所以这是我对 "answering my own question" 的最佳猜测。

现代处理器不直接执行原始 x86 指令集。相反,x86 指令集在被 Intel 内核执行之前被 "compiled" 分解为微操作 (uOps)。这应该不足为奇,因为一些 x86 指令可能很复杂。取自优化指南的例子如下:

Similarly, the following store instruction has three register sources and is broken into "generate store address" and "generate store data" sub-components.

MOV [ESP+ECX*4+12345678], AL

目前可在优化手册(2.3.2.4 微操作队列和循环流检测器 (LSD))的第 50 页找到。

本例store操作的地址比较复杂,所以是自己的uOp。所以至少,这个奇异的 x86 指令在内部被转换为两个 uOps。这两个uOps的名字是"Store Address"和"Store Data"。手册根本没有描述内部uOps,因此可能需要两个以上的uOps才能完成。

由于 Skylake 系统上只有一个 "store data" 端口,这意味着 Skylake 每个周期最多只能修改一个内存位置。三个"Store Address"端口意味着Skylake可以同时计算很多指令的有效地址(可能是因为一些非常复杂的地址可能需要多个uOp来执行??)。

自第一个 P6 系列微架构 Pentium Pro 以来,英特尔 CPU 一直将存储分为存储地址和存储数据。

但是存储地址和存储数据微指令可以微融合为一个融合域微指令。在 Sandy/IvyBridge 上,索引寻址模式是非分层的,如 Intel 优化手册中所述。但是 Haswell 和后来的人甚至可以在 ROB 中使它们保持微融合,因此它们不是未层压的。参见 Micro fusion and addressing modes. (Intel doesn't mention this, and Agner Fog hasn't had time to test extensively for Haswell/Skylake so his usually-good microarch PDF doesn't even mention un-lamination at all. But you should still definitely read it to learn more about how uops work and how instructions are decoded and go through the pipeline. See also other x86 performance links in the tag wiki)


Considering that Skylake can perform #1 3x per clock cycle, but can only perform #2 once per clock cycle

端口 2 和 3 也可以 运行 在其 AGU 上加载 uops,使端口的加载数据部分在该周期未使用。 Port7 只有一个专用的 store-AGU 用于简单的寻址模式。

带索引寄存器的存储寻址模式不能使用端口 7,只能 p2/p3。但是,如果您确实对存储使用 "simple" 寻址模式,则峰值吞吐量为每个时钟 2 个负载 + 1 个存储。


在 Nehalem 和更早版本(P6 系列)上,p2 是唯一的加载端口,p3 是存储地址端口,p4 是存储数据。

在 IvyBridge/Sandybridge 上,没有用于存储地址微指令的单独端口,它们始终只是 运行 在加载端口 (p23) 中的 AGU(地址生成单元)上。对于 256b 加载/存储,AGU 仅每隔一个周期需要一次(256b 加载或存储微指令占用加载或存储数据端口 2 个周期,但加载端口可以在第 2 个周期期间接受存储地址微指令)。因此,理论上每个时钟 2 次加载/1 次存储在 Sandybridge 上是可持续的,但 只有 如果大部分是使用 AVX 256 位矢量加载/存储 运行 宁作为两个 128 -位减半。

Haswell 在端口 7 上添加了专用存储 AGU 并将 load/store 执行单元扩展到 256b,因为如果有稳定的供应,当加载端口不需要它们的 AGU 时没有空闲周期负载。


存储地址 uop 将地址(我猜是宽度)写入存储缓冲区(在英特尔术语中也称为内存顺序缓冲区)。让这件事单独发生,甚至可能在要存储的数据准备好之前,让以后的加载(按程序顺序)检测它们是否与存储重叠。

当有未知地址的挂起存储时,加载的乱序执行是有问题的:错误的猜测意味着必须回滚管道。 (我认为 machine_clears.memory_ordering perf counter 事件包括这个。可以从单线程代码中获得非零计数,但我忘记了我是否有明确的证据表明 Skylake 有时会推测性地猜测负载不会重叠未知地址商店)。

正如 David Kanter 指出的那样 in his Haswell microarch writeup,加载 uop 还需要探测存储缓冲区以检查转发/冲突,因此只有 运行s 存储地址 uops 的执行单元是建造成本更低。

无论如何,我不确定 如果 Intel 重新设计了一些东西,那么 port7 有一个完整的 AGU,也可以处理索引寻址模式,并且只制作存储地址 uops 运行 在第 7 页,而不是 p2/p3.

这将停止来自 "stealing" p23 的存储地址微指令,这确实发生了并且将最大持续 L1D 带宽从 96 字节/周期(2 加载 + 1 存储 32 字节 YMM 向量)减少到根据英特尔优化手册中的 table,Skylake 约为 81 字节/周期。但是在正确的情况下,Skylake can sustain 2 loads + 1 store per clock of 4-byte operands,所以 81 字节/周期数可能受到其他一些微体系结构限制。峰值是 96B/clock,但显然这不可能无限期地连续发生。

从 p23 上的 运行ning 停止存储地址 uops 的一个缺点是需要更长的时间才能知道存储地址,可能会延迟加载更多。

I can't understand when store-forwarding on the data (aka: STD / Port 4) would ever be done.

A store/reload 可以让负载从存储缓冲区中获取数据,而不是等待它提交到 L1D 并从那里读取。

Store/reload 当函数在调用函数之前溢出一些寄存器时,可能会发生,作为在堆栈上传递参数的一部分(尤其是在糟糕的堆栈参数调用约定中传递堆栈上的所有参数)。或者通过引用非内联函数传递一些东西。或者在直方图中,如果重复点击同一个 bin,则基本上是在循环中执行内存目标增量。