英特尔优化手册/ B.5.7.3
解码的ICache中没有部分匹配。如果有任何微操作是缺少对32字节块的查找的一部分,已解码该交易的所有微操作都会发生ICache丢失]
uop-cache丢失是否真的以32字节粒度发生?
在KbL i7-8550U
上,其行为就像每个高速缓存行没有部分命中,不是32字节区域。
我实际上进行了下面描述的更多不同的实验,但是在这里不可能全部适合它们。
《英特尔优化手册》证明uop缓存包含L1i:
解码的ICache实际上包含在指令缓存中,ITLB。
考虑以下内容
示例1。
;edi = 1 << 31
align 32
test_uop_cache_hit:
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
;More 8 * nop ax blocks
dec edi
jnz test_uop_cache_hit
ret
收集计数器icache_64b.iftag_hit
,idq.dsb_uops
,idq.mite_uops
,下面的图
uops图是合理的。所有uops均从dsb交付。
第一张图显示,每个L1i高速缓存行只有一个标签查找,大小为64字节。标签查找对于找到uop缓存条目是必需的。
示例2。
在同一高速缓存行的8 * nop ax
个块的中间添加jmp。
;edi = 1 << 31
align 64
test_uop_cache_hit:
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
jmp test_uop_cache_hit_1
align 32
test_uop_cache_hit_1:
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
dec edi
jnz test_uop_cache_hit
ret
我们有以下情节:
uop图又是合理的。从icache_64b.iftag_hit
中得出的结论是,预测要采取的分支会引起li1标签查找,以便在uop缓存中找到相应的条目(即使分支源和目标属于同一行)。有了这个观察,Intel Optimization Manual/2.5.5.2
一旦从旧版管道中交付了微型操作程序,解码的ICache中的微操作只能在下一次之后恢复分支微操作。
对我来说看起来很合理。
现在考虑一点点有趣
示例3。
我将使用汇编程序伪代码来节省空间
align 64
test_uop_cache_hit:
8 * nop ax
19 * nop
jmp test_uop_cache_hit_1
align 32:
test_uop_cache_hit_1: ;new line starts here
;more 8 * nop ax 19 * nop jmp blocks
dec edi
jnz test_uop_cache_hit
ret
我们得到以下结果
这里有趣的是,即使插入了已插入的分支微操作,并且8 * nop ax
仍完全适合uop缓存它们没有从uop缓存中传递。从图上可以看到从uop缓存传递的唯一微操作是宏融合dec-jnz
。
结果让我觉得,如果某些32字节区域不适合uop缓存,则整个缓存行都标记为不包含在uop缓存中,下次要求它的任何32字节部分将被传递来自旧版解码管道。
要从旧版解码管道切换,是否需要分支微型操作?要检查它,请考虑
示例4。
align 32
test_uop_cache_hit:
32 * nop
test_uop_cache_hit_0: ;new line start here
16 * nop ax
;more 16 * nop ax
dec edi ;new line start here
jnz test_uop_cache_hit
ret
这是dsb的结果
很明显,所有的uops都是从旧的解码管道中传递的。
考虑一些更复杂的示例,以检查在Example 3.
下进行的假设是否在此处起作用:
I。
align 32
test_uop_cache_hit:
6 * nop ax
test edi, 0x1
;ends 64 byte region, misses due to erratum
;does not matter for the example
jnz test_uop_cache_hit_1
32 * nop
test_uop_cache_hit_1:
dec edi
jnz test_uop_cache_hit
ret
结果是
1 075 981 881 idq.dsb_uops
50 341 922 587 idq.mite_uops
结果是完全合理的。当不采用分支并传递了32 * nop
时,很明显它们无法容纳uop缓存。在32 * nop
之后,从旧版解码管道传送融合了dec-jnz
的宏。它适合uop缓存,因此,下次采用该分支时,它将从dsb传递。
结果非常接近预期:(1 << 31)/2 = 1073741824
II。
比以前更复杂的示例
align 32
test_uop_cache_hit:
test edi, 0x1
jnz test_uop_cache_hit_2
jmp test_uop_cache_hit_1
;starts new cache line
align 32
test_uop_cache_hit_1:
8 * nop ax
; 32 byte aligned
test_uop_cache_hit_2:
6 * nop ax
nop dword [eax + 1 * eax + 0x1]
;End of 32 bytes region
;misses due to erratum
;Important here
jmp test_uop_cache_hit_3
test_uop_cache_hit_3:
dec edi
jnz test_uop_cache_hit
ret
这里是结果:
5 385 033 285 idq.dsb_uops
25 815 684 426 idq.mite_uops
预期结果。每次获取dec edi - jnz test_uop_cache_hit_2
时,它都会跳转到末尾包含jmp
的32字节区域。因此它将错过dsb。下次不获取dec edi - jnz test_uop_cache_hit_2
时,获取jmp test_uop_cache_hit_1
。通常,由于8 * nop ax
非常适合dsb,因此它会命中dsb,但请记住,在上一个循环迭代中,在32字节区域末尾的jmp
会导致丢失。它们都属于同一高速缓存行,因此,每次迭代都会发生dsb丢失。
结果几乎接近预期:(1 << 31) + (1 << 31)/2 + (1 << 31) = 5368709120
。
从32字节区域中只删除一个nop ax
,并以jmp
结尾,同时保留test_uop_cache_hit_3
32字节对齐会导致所有uops从dsb传递:
29 081 868 658 idq.dsb_uops
8 887 726 idq.mite_uops
注:如果预测每个高速缓存行有2个分支,结果将是非常不可预测的,因此很难给出合理的估计。我不清楚为什么。