为什么16在主存储器中步进4K不会导致L1d缓存丢失

时间:2019-01-07 05:04:44

标签: 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

我的理解有什么问题?

1 个答案:

答案 0 :(得分:3)

所有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完全相同。

相关: