为什么在主内存中 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 映射到零页面是分开的。它总是可以分配一个真实的页面,即使是只读访问当第一次触摸页面时。)