错过了uop-cache的粒度

问题描述 投票:0回答:1

英特尔优化手册B.5.7.3

在解码ICache中没有部分命中。如果在32字节块上属于该查找的任何微操作缺失,则该事务的所有微操作都会发生解码ICache缺失。

uop-cache失误真的会发生在32字节粒度上吗?

assembly x86 x86-64 cpu-architecture
1个回答
5
投票

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 我们有以下情节

enter image description here

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

我们有如下图。

enter image description here

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 

我们有以下结果

enter image description hereenter image description here

这里有趣的是,尽管采取分支微孔插入和 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的结果

enter image description here

很明显,所有的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个分支的话,结果是很难预料的,所以很难给出一个合理的估计。我不清楚为什么。

© www.soinside.com 2019 - 2024. All rights reserved.