assembly - 为什么主内存中 16 步进 4K 不会导致 L1d 缓存未命中

标签 assembly x86 cpu-cache micro-architecture

我在 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。

相关:

关于assembly - 为什么主内存中 16 步进 4K 不会导致 L1d 缓存未命中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54068790/

相关文章:

assembly - 使用 int 13h 从硬盘读取和写入一个扇区

caching - 如何在 ARM Cortex-A7 的内核之间划分 L2 缓存?

arm - 通过虚拟地址刷新/无效范围; ARMv8;缓存;

assembly - 如何在 sparc V8 处理器中设置 TBR?

assembly - 为什么我们使用 sub $0x18, %rsp?

assembly - 向BX寄存器写入值对ES寄存器有影响吗?

performance - 计算数组中“小于x”的元素

memory - 谁在启动期间加载 BIOS 和内存映射

caching - PIPT L1 缓存也是 VIPT 的最小关联性,访问集合而不将索引转换为物理

c - gcc - 结构作为调试标签