St.*_*rio 2 x86 assembly x86-64 cpu-architecture
英特尔优化手册/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
Run Code Online (Sandbox Code Playgroud)
收集计数器icache_64b.iftag_hit, idq.dsb_uops,idq.mite_uops我们有以下情节
uops 情节是合理的。所有的 uops 都是从 dsb 发送的。
第一个图显示每个 L1i 缓存行只有一个标签查找,大小为 64 字节。为了找到 uop 缓存条目,标签查找是必要的。
示例 2。
nop ax在同一缓存行的 8 *个块的中间添加 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
Run Code Online (Sandbox Code Playgroud)
我们有以下情节:
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
Run Code Online (Sandbox Code Playgroud)
我们有以下结果
这里有趣的是,即使已插入分支微操作并8 * nop ax完美地适合 uop 缓存,它们也不会从 uop 缓存中传送。从图中可以看出,从 uop 缓存传递的唯一微操作是 macro fuseddec-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
Run Code Online (Sandbox Code Playgroud)
这是 dsb 的结果
很明显,所有的 uops 都是从传统的解码管道传送的。
考虑一些更复杂的例子来检查Example 3.那里的假设是否有效:
一世。
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
Run Code Online (Sandbox Code Playgroud)
结果是
1?075?981?881 idq.dsb_uops
50?341?922?587 idq.mite_uops
Run Code Online (Sandbox Code Playgroud)
结果是完全合理的。当分支没有被采用并且32 * nops 被传递时,很明显它们不能适合 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
Run Code Online (Sandbox Code Playgroud)
结果如下:
5?385?033?285 idq.dsb_uops
25?815?684?426 idq.mite_uops
Run Code Online (Sandbox Code Playgroud)
结果在意料之中。每次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采取。通常它会击中 dsb,因为8 * nop ax它非常适合它,但请记住,在之前的循环迭代中jmp,32 字节区域的末尾导致未命中。它们都属于同一个缓存行,因此每次迭代都会发生 dsb 未命中。
结果非常接近预期:(1 << 31) + (1 << 31)/2 + (1 << 31) = 5368709120。
在保留32 字节对齐的同时nop ax从 32 字节区域中仅删除一个,会导致所有 uops 从 dsb 传送:jmptest_uop_cache_hit_3
29?081?868?658 idq.dsb_uops
8?887?726 idq.mite_uops
Run Code Online (Sandbox Code Playgroud)
注意:如果预测每个缓存行有 2 个分支,结果将变得非常不可预测,因此很难给出合理的估计。我不清楚为什么。
| 归档时间: |
|
| 查看次数: |
118 次 |
| 最近记录: |