为什么在主内存中 16 步进 4K 导致没有 L1d 缓存未命中
Why 16 stepping by 4K in main memory causing no L1d cache miss
我在 IvyBridge 上,想测试 L1d 缓存组织。我的理解如下:
在IvyBridge上,L1d cache容量为32K,cache line 64B,8 way set associative。因此它有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
部分,它实际上将与文件支持一起映射。 (你必须让它更小,因为零实际上会在 executable 中)。
更有趣的(对我来说)结果是你做开始以更大的步幅获得缓存未命中。然后,您正在访问更多的 4k 页面,这可能会导致您的内核开始为您的 BSS 使用 2M 大页面,具有讽刺意味的是,它们不再是同一个 4k 物理页面的别名,从而伤害了它。你可以检查 /proc/PID/smaps
看看是否有非零的 AnonHuge
对于那个映射。
L2 未命中是预料之中的,因为它也只有 8 向关联,但 L3 更具关联性并且使用非简单的索引函数,该函数将 2 步长的任何简单幂分布到多个集合。 ()
顺便说一句,您可能想要一个不是 2 的幂的间隙。只是 L1 别名步幅的倍数,而不是 L2 别名步幅的倍数,因此您的数据可以分布在 L2 中的许多集合中。
我一直在寻找重复项,但没有找到确切的副本,尽管我很确定我已经在 SO >.< 的某处解释过这一点。可能我正在考虑 那里与 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)?(惰性分配/甚至不设置页面 table 最初与 COW 映射到零页面是分开的。它总是可以分配一个真实的页面,即使是只读访问当第一次触摸页面时。)
我在 IvyBridge 上,想测试 L1d 缓存组织。我的理解如下:
在IvyBridge上,L1d cache容量为32K,cache line 64B,8 way set associative。因此它有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
部分,它实际上将与文件支持一起映射。 (你必须让它更小,因为零实际上会在 executable 中)。
更有趣的(对我来说)结果是你做开始以更大的步幅获得缓存未命中。然后,您正在访问更多的 4k 页面,这可能会导致您的内核开始为您的 BSS 使用 2M 大页面,具有讽刺意味的是,它们不再是同一个 4k 物理页面的别名,从而伤害了它。你可以检查 /proc/PID/smaps
看看是否有非零的 AnonHuge
对于那个映射。
L2 未命中是预料之中的,因为它也只有 8 向关联,但 L3 更具关联性并且使用非简单的索引函数,该函数将 2 步长的任何简单幂分布到多个集合。 (
顺便说一句,您可能想要一个不是 2 的幂的间隙。只是 L1 别名步幅的倍数,而不是 L2 别名步幅的倍数,因此您的数据可以分布在 L2 中的许多集合中。
我一直在寻找重复项,但没有找到确切的副本,尽管我很确定我已经在 SO >.< 的某处解释过这一点。可能我正在考虑
相关:
- 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)?(惰性分配/甚至不设置页面 table 最初与 COW 映射到零页面是分开的。它总是可以分配一个真实的页面,即使是只读访问当第一次触摸页面时。)