我试图了解我的 Haswell 芯片上的 uop-cache(intel 文档中的 DSB)的行为。我以英特尔优化手册和 Agner pdf 为基础。
我发现了一组案例,其中前端可靠地回退到 MITE 解码器,这取决于代码中的微小变化,这让我感到困惑。
一个例子看起来像这样(在 gnu as 中-msyntax=intel
):
mov rax, 100000000
.align 64
1:
// DSB-cacheable nops to
// overflow LSD
.fill 12, 1, 0x90
.align 32
.fill 12, 1, 0x90
.align 32
.fill 12, 1, 0x90
.align 32
.fill 12, 1, 0x90
.align 32
// this first block should fill up way 1 of our uop-cache set
add ecx, ecx
nop
nop
nop
add ecx, ecx
add ecx, ecx
// way 2
add ecx, ecx
add ecx, ecx
add ecx, ecx
nop
add ecx, ecx
add ecx, ecx
// way 3
or ecx, ecx
or ecx, ecx // <---- an example of offending instruction
or ecx, ecx
or ecx, ecx
or ecx, ecx
or ecx, ecx // <---- this one as well
// next uop set
dec rax
jnz 1b
显然,这是一个荒谬的例子。我通常将它作为包含足够其他 32 字节块以溢出 LSD 以使事情更简单的循环的一部分包含在内,但这不是 AFAICT 所必需的。出于类似的原因,我将此块设置为正好 32 个字节,以排除与悬空指令相关的任何内容。
我使用两个相应的性能计数器(IDQ.MITE_UOPS 和 IDQ.DSB_UOPS)来测量 DSB 与 MITE 的使用情况。
在写入时,此 32 字节块将缓存在 DSB 中,但是将其中一个标记更改or ecx, ecx
为 anadd ecx, ecx
足以触发传统解码器。
这让我感到惊讶,因为两条指令具有相同的大小并且都生成 1 uop。
事实上,在玩类似的例子时,我发现在触发或不触发不同缓存行为的指令之间的唯一共同点是它们是否会与分支进行宏融合(如果有的话)。
我在任何地方都找不到此(或任何相关)行为的描述,是否有我遗漏的东西?