首页 > 解决方案 > AVX-512 多核而非单核上的缓存命中和未命中

问题描述

以下是 NASM 程序的循环体(循环体意味着我没有显示实例化内核和共享内存、读取输入数据、将最终结果写入文件的部分)。该程序是从 C 包装器调用的共享对象。其中九行显示了行号;它们对应于以下注释中引用的行号。

mov rax,255
kmovq k7,rax

label_401:

    cmp r11,r10
    jge label_899

    vmovupd zmm14,[r12+r11] ;[185]
    add r11,r9 ; stride ;[186]

    vmulpd zmm13,zmm14,zmm31 ; [196]

    vmulpd zmm9,zmm14,zmm29 ; [207]
    vmulpd zmm8,zmm13,zmm30

    mov r8,1
    Exponent_Label_0:
    vmulpd zmm7,zmm29,zmm29
    add r8,1
    cmp r8,2 ;rdx
    jl Exponent_Label_0

    vmulpd zmm3,zmm7,zmm8

    vsubpd zmm0,zmm9,zmm3

    vmulpd zmm1,zmm0,zmm28
    VCVTTPD2QQ zmm0{k7},zmm1 ; [240]
    VCVTUQQ2PD zmm2{k7},zmm0 ; [241]
    vsubpd zmm3,zmm1,zmm2
    vmulpd zmm4,zmm3,zmm27 ; [243]
    VCVTTPD2QQ zmm5{k7}{z},zmm4

    VPCMPGTQ k2,zmm5,zmm26
    VPCMPEQQ k3 {k7},zmm5,zmm26
    KADDQ k1,k2,k3

    VCVTQQ2PD zmm2{k7},zmm0 ; [252]
    vmulpd zmm1{k7},zmm2,zmm25
    vmovupd zmm2,zmm1
    VADDPD zmm2{k1},zmm1,zmm25

    vmovapd [r15+r14],zmm2 ; [266]
    add r14,r9 ; stride
    jmp label_401

该程序仅在第 185 行读取的数据与第 266 行将最终结果写入共享内存缓冲区之间使用 AVX-512 寄存器到寄存器指令。我用 1 个内核和 4 个内核运行它,但 4 -core 版本比单核慢 2-3 倍。我使用 Linux perf 对其进行了分析,以了解为什么 AVX-512 多核比单核慢 2-3 倍。

下面显示的性能报告是通过使用 perf record/perf annotate 运行所有 65 个 PEBS 计数器来完成的——按源代码行查看结果——并使用 perf stat 来获得完整计数。每个 perf 记录和 perf stat 计数器都是单独运行的,结果按源代码行汇总,perf stat 的计数显示在每个下方。

每条指令后跟源代码行号。对于 perf 记录指令,它在每行末尾的括号中显示该计数器占源代码行的百分比,以及此类指令的总数(来自 perf stat)。

我的主要问题是为什么我们在 AVX-512 指令上看到多核缓存命中和未命中都是寄存器到寄存器指令,但在单核上却没有相同的指令。对于完全在寄存器中的指令,不应有任何高速缓存命中或未命中。每个内核都有自己的一组寄存器,所以我不希望任何指令都是寄存器到寄存器的缓存活动。当仅使用单个内核运行时,我们在所有寄存器指令中几乎看不到缓存活动。

1.  Line 186 - add r11,r9
mem_inst_retired.all_loads     75.00% (447119383)
mem_inst_retired.all_stores    86.36% (269650353)
mem_inst_retired.split_loads   71.43% (6588771)
mem_load_retired.l1_hit        57.14% (443561879)

Single core (line 177) - add r11,r9
mem_inst_retired.all_stores    24.00% (267231461)

这条指令(add r11,r9)增加了两个寄存器。当使用单核运行时,我们看不到任何缓存命中/未命中或内存负载,但使用多核时我们会看到。为什么这里有多核而不是单核的缓存命中和内存加载指令?

2.  Line 196 - vmulpd zmm13,zmm14,zmm31
mem_inst_retired.split_loads     28.57% (6588771)
mem_load_retired.fb_hit         100.00% (8327967)
mem_load_retired.l1_hit         14.29% (443561879)
mem_load_retired.l1_miss        66.67% (11033416)

Single core (line 187) - vmulpd zmm13,zmm14,zmm31
mem_load_retired.fb_hit 187 100.00% (8889146)

该指令 (vmulpd zmm13,zmm14,zmm31) 是所有寄存器,但它再次显示 L1 命中和未命中以及多核而不是单核的拆分负载。

3. Line 207 - vmulpd zmm9,zmm14,zmm29
mem_load_retired.l1_hit           14.29% (443561879)
mem_load_retired.l1_miss          33.33% (11033416)
rs_events.empty_end               25.00% (37013411)

Single core (line 198):
mem_inst_retired.all_stores       24.00% (267231461)
mem_inst_retired.stlb_miss_stores 22.22%

此指令 (vmulpd zmm9,zmm14,zmm29) 与上面描述的指令 (vmulpd, all registers) 相同,但它再次显示 L1 命中和未命中以及多核而不是单核的拆分负载。单核确实显示了二级 TLB 未命中和存储指令退出,但没有缓存活动。

4.  Line 240 - VCVTTPD2QQ zmm0{k7},zmm1
mem_inst_retired.all_loads               23.61% (447119383)
mem_inst_retired.split_loads             26.67% (6588771)
mem_load_l3_hit_retired.xsnp_hitm        28.07% (1089506)
mem_load_l3_hit_retired.xsnp_none        12.90% (1008914)
mem_load_l3_miss_retired.local_dram      40.00% (459610)
mem_load_retired.fb_hit                  29.21% (8327967)
mem_load_retired.l1_miss                 19.82% (11033416)
mem_load_retired.l2_hit                  10.22% (12323435)
mem_load_retired.l2_miss                 24.84% (2606069)
mem_load_retired.l3_hit                  19.70% (700800)
mem_load_retired.l3_miss                 21.05% (553670)

Single core line 231:
mem_load_retired.l1_hit                  25.00% (429499496)
mem_load_retired.l3_hit                  50.00% (306278)

此行 (VCVTTPD2QQ zmm0{k7},zmm1) 是寄存器到寄存器。单核显示 L1 和 L3 活动,但多核具有更多的缓存活动。

5.  Line 241 - VCVTUQQ2PD zmm2{k7},zmm0
mem_load_l3_hit_retired.xsnp_hitm        21.05% (1089506)
mem_load_l3_miss_retired.local_dram      10.00% (459610)
mem_load_retired.fb_hit                  10.89% (8327967)
mem_load_retired.l2_miss                 13.07% (2606069)
mem_load_retired.l3_miss                 10.53%

Single core line 232:
Single core has no cache hits or misses reported
mem_load_retired.l1_hit                  12.50% (429499496)

全寄存器指令 (VCVTUQQ2PD zmm2{k7},zmm0) 显示了多核的大量缓存活动,但单核只有少量的 L1 命中 (12.5%)。我不希望使用全寄存器指令看到任何缓存命中/未命中或加载/存储指令。

6.  Line 243 - vmulpd zmm4,zmm3,zmm27
br_inst_retired.all_branches_pebs        12.13% (311104072)

Single core line 234:
mem_load_l3_hit_retired.xsnp_none        100.00% (283620)

为什么我们会看到全寄存器 mul 指令的分支指令?

7.  Line 252 - VCVTQQ2PD zmm2{k7},zmm0
br_inst_retired.all_branches_pebs     16.62% (311104072)
mem_inst_retired.all_stores           21.22% (269650353)

Single core line 243:
Single core also has branch instructions
br_inst_retired.all_branches_pebs     22.16% (290445009)

对于寄存器到寄存器指令(VCVTQQ2PD zmm2{k7},zmm0),为什么我们会看到分支指令?该指令不分支,也不在分支之前或之后。

8.  Line 266 - vmovapd [r15+r14],zmm2
br_inst_retired.all_branches_pebs 43.56% (311104072)
mem_inst_retired.all_loads        48.67% (447119383)
mem_inst_retired.all_stores       43.09% (269650353)
mem_inst_retired.split_loads      41.30% (6588771)
mem_inst_retired.stlb_miss_loads  11.36% (487591)
mem_inst_retired.stlb_miss_stores 12.50% (440729)
mem_load_l3_hit_retired.xsnp_hitm 33.33% (1089506)
mem_load_l3_hit_retired.xsnp_none 56.45% (1008914)
mem_load_l3_miss_retired.local_dram 35.00% (459610)
mem_load_retired.fb_hit             39.60% (8327967)
mem_load_retired.l1_hit             48.75% (443561879)
mem_load_retired.l1_miss            51.65% (11033416)
mem_load_retired.l2_hit             71.51% (12323435)
mem_load_retired.l2_miss            45.10% (2606069)
mem_load_retired.l3_hit             59.09% (700800)
mem_load_retired.l3_miss            47.37% (553670)

Single core line 257:
mem_inst_retired.all_loads          84.86% (426023012)
mem_inst_retired.all_loads 
mem_inst_retired.all_stores         59.28% (267231461)
mem_inst_retired.split_loads        89.92% (6477955)
mem_load_l3_miss_retired.local_dram 100.00% (372586)
mem_load_retired.fb_hit              92.80% (8889146)
mem_load_retired.l1_hit              54.17% (429499496)
mem_load_retired.l1_miss             91.30% (4170386)
mem_load_retired.l2_hit             100.00% (4564407)
mem_load_retired.l2_miss            100.00% (476024)
mem_load_retired.l3_hit              33.33% (306278)

这行 (vmovapd [r15+r14],zmm2) 可能是最有可能影响单核和多核之间差异的行。在这里,我们将最终结果传输到所有内核共享的内存缓冲区。因为存在内存移动,我们希望看到多核和单核的缓存活动。单核使用使用 malloc 创建的单个缓冲区。对于多核,它是 posix 共享内存,因为它的运行速度明显快于使用 malloc 创建的数组。

单核和多核都在 Intel Xeon Gold 6140 CPU @ 2.30GHz 上运行,它有两个用于 AVX-512 的 FMA 单元。

总而言之,我的问题是:(1)为什么我们在 AVX-512 多核而不是单核的寄存器到寄存器指令上看到缓存活动(极少数情况除外);(2) 有没有办法在 vmovapd [r15+r14],zmm2 处完全绕过缓存并直接进入内存以避免缓存未命中?Posix 共享内存是一种改进,但并没有完全做到这一点。最后,还有其他原因导致多核 AVX-512 比单核慢得多吗?

更新:此代码的访问模式由 AVX 规定 - 步幅是(64 x 核心数)字节。4核,核0从0开始,读取并处理64字节,然后跳转256(64x4);核心 1 从 64 开始,读取和处理 64 字节,然后跳转 256,等等。

标签: performancememoryparallel-processingx86perf

解决方案


推荐阅读