Elr*_*nnd 5 performance x86 assembly cpu-architecture micro-optimization
Agner发现,在大多数支持这些指令的处理器上,应用于内存操作数的 x86 位操作指令(btr bts btc、无锁)比其他读-修改-写指令(如 add、xor 等)慢。为什么是这样?这些说明似乎非常容易实施。
是否因为实际加载的地址与内存操作数指定的地址不同,这混淆了一些跟踪内存访问的前端机制?这似乎是合理的,但我不认为它会影响吞吐量(至少不会影响那么多);只有延迟。
是不是因为实际加载的地址和内存操作数指定的地址不一样
是的,很明显,这就是它与记忆目的地转变的区别。
reg-reg 版本在 Intel 上为 1 uop,具有 1 个周期延迟,在 Intel Haswell 及更高版本上的执行端口 0 或 6 上运行,与移位相同。(将索引解码为 1-hot mask 比一般移位器便宜,但由于存在移位单元,大概英特尔只使用这些单元。)
AMD 由于某种原因bts reg,reg以 2 uop 运行,比简单的移位慢。我不知道为什么,也许与标志设置有关。
bts mem, imm8也很正常,Intel 上有 3 个前端 uops。 xor mem, imm8只有2个前端uop,但那是因为它可以微熔合负载+异或。 not mem是3个前端uop,仅微融合store-address和store-uop指令。
这会混淆一些跟踪内存访问的前端机制吗?
不。前端不跟踪内存访问,那是后端。
它的部分速度很慢,因为它是作为多个微指令实现的;即使你做的事情周围有不同的说明,这也会很痛苦。在 Intel Haswell 和 Alder Lake(可能介于两者之间)上, 的前端微指令为 10 bts mem, r32,而 的前端微指令为 3bts mem, imm8
由于它不能直接使用通常的地址生成硬件,因此它在微代码中实现为多个微指令,大概是从正常寻址模式将 LEA 转换为临时数据,然后添加到其中以通过(bit_index>>6) * 4双字或类似的方式进行索引。哦,也许它是 10 uops 的原因是它总是想要访问包含该位的对齐双字,而不仅仅是寻址模式中地址的 4 倍数偏移量,[]例如[rax + rdx*4 + 123].
对于您知道位串的开头已对齐的正常情况,手动执行此操作会更有效,因此您可以通过位索引来获取用于加载/ (1 uop)/存储的shr双字索引。bts reg,reg这比 需要更少的微指令bts [mem], reg。请注意,bts reg,reg截断/包装位索引,因此如果您正确安排事情,则模数是免费的。例如埃拉托色尼筛。另外,内存目标 BTS 为何会比加载 / BTS reg、reg / store 慢得多?
但 Agner Fog 和https://uops.info/都测量了 Haswell / Alder Lake P 核上 5 个周期的吞吐量,显着低于前端瓶颈(或任何每端口后端瓶颈)所占的比例。
我不知道是什么原因造成的。 实际的加载和存储微指令应该是正常的,输入来自内部临时寄存器,但就存储缓冲区和加载缓冲区中的地址而言,仍然是正常的加载微指令和存储微指令。(英特尔称内存顺序缓冲区 = MOB。)
我不希望它是内存依赖性预测的特殊情况,因为这种情况发生在加载微指令执行时(并且之前的存储地址微指令尚未执行,因此地址是一些先前的存储仍然未知。)
TODO:运行一些实验,看看是否有任何其他指令混合在一起bts mem,reg会减慢它的速度,竞争它所占用的任何资源。
它看起来不像https://uops.info/的基准测试错误(例如每次使用相同的地址并在存储转发延迟上停滞)。他们的测试包括一些使用不同偏移量的展开序列。例如,Haswell 吞吐量测试bts m64, r64每次使用相同地址测量 6.02 或 6.0 个周期吞吐量 ( ) bts qword ptr [r14], r8,或展开重复序列时每个 BTS 平均 5.0 个周期,如bts [r14],r8;bts [r14+0x8],r8; ...; bts [r14+0x38],r8。即使对于覆盖两个相邻高速缓存行的 16 个独立指令的序列,每次迭代仍然是相同的 5 个周期。
| 归档时间: |
|
| 查看次数: |
276 次 |
| 最近记录: |