我在 IvyBridge 上,想要测试 L1d 缓存组织。我的理解如下:
在IvyBridge上,L1d缓存有32K容量,64B缓存线,8路组相联。因此它有 32K/(64*8) = 64 个集合,给定一个主存 addr
,集合索引可以通过 (addr/64) % 64
计算出来。
因此,如果我将主内存步进 64*64 (4K),我将始终接触相同的 L1d 集。一个集合只有 8 个缓存行,因此如果我用 16 个步骤循环它,我将获得几乎 100% 的 L1d 缓存未命中。
我编写了以下程序来验证:
section .bss
align 4096
buf: resb 1<<26
%define gap 64 * 64 ; no L1 cache miss
; %define gap 64 * 64 * 256 ; 41% L1 cache miss
; %define gap 64 * 64 * 512 ; 95% L1 cache miss
; however, total cycle suggests this gap is already at L3 latency level with complete L2 cache miss.
section .text
global _start
_start:
mov rcx, 10000000
xor rax, rax
loop:
mov rax, [buf+rax]
mov rax, [buf+rax+gap*1]
mov rax, [buf+rax+gap*2]
mov rax, [buf+rax+gap*3]
mov rax, [buf+rax+gap*4]
mov rax, [buf+rax+gap*5]
mov rax, [buf+rax+gap*6]
mov rax, [buf+rax+gap*7]
mov rax, [buf+rax+gap*8]
mov rax, [buf+rax+gap*9]
mov rax, [buf+rax+gap*10]
mov rax, [buf+rax+gap*11]
mov rax, [buf+rax+gap*12]
mov rax, [buf+rax+gap*13]
mov rax, [buf+rax+gap*14]
mov rax, [buf+rax+gap*15]
dec rcx,
jne loop
xor rdi, rdi
mov rax, 60
syscall
令我惊讶的是,perf
显示根本没有丢失 L1 缓存:
160,494,057 L1-dcache-loads
4,290 L1-dcache-load-misses # 0.00% of all L1-dcache hits
我的理解有什么问题吗?
最佳答案
所有 BSS 页最初都以写时复制方式映射到同一物理零页。您会遇到 TLB 未命中(可能还有软页面错误),但不会发生 L1d 未命中。
为了避免这种情况并将它们映射到不同的物理页面:
- 首先通过向每个页面写入一个字节来弄脏它们
- 也许可以使用
mmap(MAP_POPULATE)
进行分配,而不是使用 BSS。这至少会预先使它们出现故障,避免软页面错误,但可能仍然会出现相同的物理零页。 - 将
buf
放入.data
或.rodata
部分,实际上它将与文件支持进行映射。 (您必须使其小得多,因为零实际上位于可执行文件中)。
(对我来说)更有趣的结果是,您确实开始以更大的步幅获得缓存未命中。然后,您将访问更多的 4k 页面,这可能会导致您的内核开始为 BSS 使用 2M 大页面,讽刺的是,它们不再是同一个 4k 物理页面的别名,从而损害了它。您可以检查/proc/PID/smaps 来查看是否有非零 AnonHuge 对于该映射。
L2 未命中是预料之中的,因为它也只是 8 路关联,但 L3 更具关联性,并使用非简单索引函数,将 2 步幅的任何简单幂分布到多个集合上。 (Which cache mapping technique is used in intel core i7 processor?)
顺便说一句,您可能需要一个不是 2 的幂的间隙。只是 L1 混叠步长的倍数,而不是 L2 混叠步长的倍数,这样您的数据就可以分布在 L2 中的多个集合中。
我一直在寻找重复项,但没有找到精确的重复项,尽管我很确定我之前已经在 SO >.< 上的某个地方解释过这一点。可能我在想How can I obtain consistently high throughput in this loop?这是与 malloc 完全相同的问题,而不是 BSS。
相关:
- Is it true, that modern OS may skip copy when realloc is called有一些关于
mremap
的虚拟内存/COW 的好东西。 - Does malloc lazily create the backing pages for an allocation on Linux (and other platforms)? (惰性分配/甚至最初不设置页表与 COW 映射到零页是分开的。即使在第一次触及页面时进行只读访问,它也始终可以分配真实页面。)
- How exactly does copy on write work
关于assembly - 为什么主内存中 16 步进 4K 不会导致 L1d 缓存未命中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54068790/