英特尔优化手册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.在L1i缓存行中间添加jmp。
在8 *中间添加jmp。nop ax
块的同一缓存行。
;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-cache,整个cache行就会被标记为不包含在uop cache中,下一次询问任何32字节的部分时,就会从Legacy Decoded Pipeline传送过来。
从遗留解码管道切换到分支微操作是否必要?要检查它,请考虑
例4.下面是dsb的结果。
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
是由Legacy Decode Pipeline交付的。它适合于uop缓存,因此下一次采取分支时,它将从dsb交付。
结果非常接近预期。(1 << 31)/2 = 1073741824
二.
比之前的例子更复杂
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
的32字节区域,它将跳转到包含有 jmp
在它的最后。所以会错过dsb。下次 dec edi - jnz test_uop_cache_hit_2
不取 jmp test_uop_cache_hit_1
是采取。正常情况下,它会打出dsb,因为...。8 * nop ax
完全适合它,但请记住,在上一个循环迭代中的 jmp
在32字节区域的末尾,造成了遗漏。它们都属于同一条缓存线,因此每次迭代都会发生dsb miss。
结果是每一次都接近预期。(1 << 31) + (1 << 31)/2 + (1 << 31) = 5368709120
.
只删除一个 nop ax
从32字节的区域用 jmp
末了 test_uop_cache_hit_3
32 字节对齐后,所有的 UOPS 都会从 dsb 传送。
29 081 868 658 idq.dsb_uops
8 887 726 idq.mite_uops
注意: 如果每条缓存线有2个分支的话,结果是很难预料的,所以很难给出一个合理的估计。我不清楚为什么。