L2 硬件预取器真的有用吗?

St.*_*rio 10 c performance assembly x86-64 avx

我在Whiskey Lake i7-8565U 上分析性能计数器和复制 512 KiB 数据(比 L2 缓存大小大两倍)的时间,并在 L2 硬件预取器的工作方面遇到了一些误解。

在Intel Manual Vol.4 MSR 中,MSR0x1A4的位 0 用于控制 L2 HW 预取器(1 表示禁用)。


考虑以下基准:

memcopy.h:

void *avx_memcpy_forward_lsls(void *restrict, const void *restrict, size_t);
Run Code Online (Sandbox Code Playgroud)

memcopy.S:

avx_memcpy_forward_lsls:
    shr rdx, 0x3
    xor rcx, rcx
avx_memcpy_forward_loop_lsls:
    vmovdqa ymm0, [rsi + 8*rcx]
    vmovdqa [rdi + rcx*8], ymm0
    vmovdqa ymm1, [rsi + 8*rcx + 0x20]
    vmovdqa [rdi + rcx*8 + 0x20], ymm1
    add rcx, 0x08
    cmp rdx, rcx
    ja avx_memcpy_forward_loop_lsls
    ret
Run Code Online (Sandbox Code Playgroud)

main.c:

#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <x86intrin.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "memcopy.h"

#define ITERATIONS 1000
#define BUF_SIZE 512 * 1024

_Alignas(64) char src[BUF_SIZE];
_Alignas(64) char dest[BUF_SIZE];

static void __run_benchmark(unsigned runs, unsigned run_iterations,
                    void *(*fn)(void *, const void*, size_t), void *dest, const void* src, size_t sz);

#define run_benchmark(runs, run_iterations, fn, dest, src, sz) \
    do{\
        printf("Benchmarking " #fn "\n");\
        __run_benchmark(runs, run_iterations, fn, dest, src, sz);\
    }while(0)

int main(void){
    int fd = open("/dev/urandom", O_RDONLY);
    read(fd, src, sizeof src);
    run_benchmark(20, ITERATIONS, avx_memcpy_forward_lsls, dest, src, BUF_SIZE);
}

static inline void benchmark_copy_function(unsigned iterations, void *(*fn)(void *, const void *, size_t),
                                               void *restrict dest, const void *restrict src, size_t sz){
    while(iterations --> 0){
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
        fn(dest, src, sz);
    }
}

static void __run_benchmark(unsigned runs, unsigned run_iterations,
                    void *(*fn)(void *, const void*, size_t), void *dest, const void* src, size_t sz){
    unsigned current_run = 1;
    while(current_run <= runs){
        benchmark_copy_function(run_iterations, fn, dest, src, sz);
        printf("Run %d finished\n", current_run);
        current_run++;
    }
}
Run Code Online (Sandbox Code Playgroud)

考虑编译的 2 次运行 main.c

我。

MSR:

$ sudo rdmsr -p 0 0x1A4
0
Run Code Online (Sandbox Code Playgroud)

Run:

$ taskset -c 0 sudo ../profile.sh ./bin 

 Performance counter stats for './bin':

    10?486?164?071      L1-dcache-loads                                               (12,13%)
    10?461?354?384      L1-dcache-load-misses     #   99,76% of all L1-dcache hits    (12,05%)
    10?481?930?413      L1-dcache-stores                                              (12,05%)
    10?461?136?686      l1d.replacement                                               (12,12%)
    31?466?394?422      l1d_pend_miss.fb_full                                         (12,11%)
   211?853?643?294      l1d_pend_miss.pending                                         (12,09%)
     1?759?204?317      LLC-loads                                                     (12,16%)
            31?007      LLC-load-misses           #    0,00% of all LL-cache hits     (12,16%)
     3?154?901?630      LLC-stores                                                    (6,19%)
    15?867?315?545      l2_rqsts.all_pf                                               (9,22%)
                 0      sw_prefetch_access.t1_t2                                      (12,22%)
         1?393?306      l2_lines_out.useless_hwpf                                     (12,16%)
     3?549?170?919      l2_rqsts.pf_hit                                               (12,09%)
    12?356?247?643      l2_rqsts.pf_miss                                              (12,06%)
                 0      load_hit_pre.sw_pf                                            (12,09%)
     3?159?712?695      l2_rqsts.rfo_hit                                              (12,06%)
     1?207?642?335      l2_rqsts.rfo_miss                                             (12,02%)
     4?366?526?618      l2_rqsts.all_rfo                                              (12,06%)
     5?240?013?774      offcore_requests.all_data_rd                                     (12,06%)
    19?936?657?118      offcore_requests.all_requests                                     (12,09%)
     1?761?660?763      offcore_response.demand_data_rd.any_response                                     (12,12%)
       287?044?397      bus-cycles                                                    (12,15%)
    36?816?767?779      resource_stalls.any                                           (12,15%)
    36?553?997?653      resource_stalls.sb                                            (12,15%)
    38?035?066?210      uops_retired.stall_cycles                                     (12,12%)
    24?766?225?119      uops_executed.stall_cycles                                     (12,09%)
    40?478?455?041      uops_issued.stall_cycles                                      (12,05%)
    24?497?256?548      cycle_activity.stalls_l1d_miss                                     (12,02%)
    12?611?038?018      cycle_activity.stalls_l2_miss                                     (12,09%)
        10?228?869      cycle_activity.stalls_l3_miss                                     (12,12%)
    24?707?614?483      cycle_activity.stalls_mem_any                                     (12,22%)
    24?776?110?104      cycle_activity.stalls_total                                     (12,22%)
    48?914?478?241      cycles                                                        (12,19%)

      12,155774555 seconds time elapsed

      11,984577000 seconds user
       0,015984000 seconds sys
Run Code Online (Sandbox Code Playgroud)

二、

MSR:

$ sudo rdmsr -p 0 0x1A4
1
Run Code Online (Sandbox Code Playgroud)

Run:

$ taskset -c 0 sudo ../profile.sh ./bin

 Performance counter stats for './bin':

    10?508?027?832      L1-dcache-loads                                               (12,05%)
    10?463?643?206      L1-dcache-load-misses     #   99,58% of all L1-dcache hits    (12,09%)
    10?481?296?605      L1-dcache-stores                                              (12,12%)
    10?444?854?468      l1d.replacement                                               (12,15%)
    29?287?445?744      l1d_pend_miss.fb_full                                         (12,17%)
   205?569?630?707      l1d_pend_miss.pending                                         (12,17%)
     5?103?444?329      LLC-loads                                                     (12,17%)
            33?406      LLC-load-misses           #    0,00% of all LL-cache hits     (12,17%)
     9?567?917?742      LLC-stores                                                    (6,08%)
     1?157?237?980      l2_rqsts.all_pf                                               (9,12%)
                 0      sw_prefetch_access.t1_t2                                      (12,17%)
           301?471      l2_lines_out.useless_hwpf                                     (12,17%)
       218?528?985      l2_rqsts.pf_hit                                               (12,17%)
       938?735?722      l2_rqsts.pf_miss                                              (12,17%)
                 0      load_hit_pre.sw_pf                                            (12,17%)
         4?096?281      l2_rqsts.rfo_hit                                              (12,17%)
     4?972?640?931      l2_rqsts.rfo_miss                                             (12,17%)
     4?976?006?805      l2_rqsts.all_rfo                                              (12,17%)
     5?175?544?191      offcore_requests.all_data_rd                                     (12,17%)
    15?772?124?082      offcore_requests.all_requests                                     (12,17%)
     5?120?635?892      offcore_response.demand_data_rd.any_response                                     (12,17%)
       292?980?395      bus-cycles                                                    (12,17%)
    37?592?020?151      resource_stalls.any                                           (12,14%)
    37?317?091?982      resource_stalls.sb                                            (12,11%)
    38?121?826?730      uops_retired.stall_cycles                                     (12,08%)
    25?430?699?605      uops_executed.stall_cycles                                     (12,04%)
    41?416?190?037      uops_issued.stall_cycles                                      (12,04%)
    25?326?579?070      cycle_activity.stalls_l1d_miss                                     (12,04%)
    25?019?148?253      cycle_activity.stalls_l2_miss                                     (12,03%)
         7?384?770      cycle_activity.stalls_l3_miss                                     (12,03%)
    25?442?709?033      cycle_activity.stalls_mem_any                                     (12,03%)
    25?406?897?956      cycle_activity.stalls_total                                     (12,03%)
    49?877?044?086      cycles                                                        (12,03%)

      12,231406658 seconds time elapsed

      12,226386000 seconds user
       0,004000000 seconds sys
Run Code Online (Sandbox Code Playgroud)

我注意到柜台:

12?611?038?018 cycle_activity.stalls_l2_miss 比/秒
25?019?148?253 cycle_activity.stalls_l2_miss

表明正在应用禁用 L2 硬件预取器的 MSR。其他与 l2/LLC 相关的东西也有很大不同。这种差异在不同的运行中是可重现的。问题是total time和 周期几乎没有区别:

48?914?478?241 cycles 比/秒
49?877?044?086 cycles

12,155774555 seconds time elapsed 比/秒
12,231406658 seconds time elapsed

问题:
其他性能限制器是否隐藏了 L2 未命中?
如果是这样,你能建议看什么柜台来理解吗?

Pet*_*des 5

是的,L2 流媒体在很多时候确实很有帮助。

memcpy 没有任何要隐藏的计算延迟,所以我想它可以让 OoO exec 资源(ROB 大小)处理您从更多 L2 未命中中获得的额外加载延迟,至少在这种情况下,您从中获得所有 L3 命中使用适合 L3 的中型工作集 (1MiB),无需预取即可实现 L3 命中。

唯一的指令是加载/存储(和循环开销),因此 OoO 窗口包括相当远的需求加载。

IDK 如果 L2 空间预取器和 L1d 预取器在这里有帮助。


测试这个假设的预测:让你的阵列更大,这样你就会得到 L3 未命中,一旦 OoO exec 不足以隐藏一直到 DRAM 的加载延迟,你可能会看到总体时间的差异。提前触发硬件预取会有所帮助。

硬件预取的另一个重要好处是它可以跟上您的计算,因此您可以获得 L2 命中。(在具有中等长度但不是循环携带的依赖链的计算的循环中。)

当 ROB 容量没有其他压力时,按需加载和 OoO exec 可以使用可用(单线程)内存带宽做很多事情。


另请注意,在 Intel CPU 上,每次缓存未命中都可能会花费从属uops的后端重放(来自 RS/调度程序),当数据预计到达时,L1d 和 L2 未命中各一个。在那之后,显然核心在等待来自 L3 的数据到达时乐观地发送垃圾邮件。

(参见https://chat.stackoverflow.com/rooms/206639/discussion-on-question-by-beeonrope-are-load-ops-deallocated-from-the-rs-when-th和是否从RS 当他们派遣、完成或其他时间?)

不是缓存未命中加载本身;在这种情况下,它将是商店说明。更具体地说,端口 4 的存储数据 uop。这在这里无关紧要;使用 32 字节存储和 L3 带宽瓶颈意味着我们不会接近每个时钟 1 个端口 4 uop。

  • @St.Antario:通过寄存器重命名!这是 OoO exec 最关键的部分之一,特别是在像 x86 这样寄存器匮乏的 ISA 上。参见【为什么mulss在Haswell上只需要3个周期,与Agner的指令表不同?(使用多个累加器展开 FP 循环)](//stackoverflow.com/q/45113527)。顺便说一句,通常您会想要先执行 2 次加载,然后执行 2 次存储,而不是加载/存储加载/存储。避免或减轻 4k 混叠停顿的机会更大,因为后面的负载(硬件必须检测是否与先前的存储重叠)距离更远。 (4认同)
  • 我很惊讶预取对 L3 中的 memcpy 没有帮助。我想在这种情况下 10/12 LFB 就“足够了”。但看起来很奇怪:那里的限制因素是什么?核心 -&gt; L2 时间应该小于 L2 -&gt; L3 时间,因此在我的心智模型中,为第二条腿提供更多缓冲区(更多总占用率)应该会有所帮助。 (3认同)
  • @St.Antario:嗯?这是没有意义的; 你受到内存限制,所以你没有前端瓶颈,所以 LSD 是无关紧要的。(它避免了从 uop 缓存中重新获取它们,从而节省了一些电量)。他们仍然在 ROB 中占有一席之地,直到他们退休。它们虽然没有“那么”重要,但也不容忽视。 (2认同)
  • _让你的数组更大,这样你就会得到 L3 未命中,你可能会看到差异_我用 `16MiB` 缓冲区和 `10` 迭代运行了许多测试,确实得到了 `14,186868883 秒` 与 `43,731360909 秒` 和“所有 LL 缓存命中的 46.76%”与“所有 LL 缓存命中的 99.32%”;“1 028 664 372 LLC 负载”与“1 587 454 298 LLC 负载”。 (2认同)
  • @St.Antario:是的,当然。Agner Fog 的优化指南还通过寄存器重命名解释了 OoO exec,维基百科也是如此。顺便说一句,寄存器重命名还避免了 WAW 危险,只留下真正的依赖关系(RAW)。因此,加载甚至可以无序完成,而无需等待先前的加载完成“写入”相同的架构寄存器。是的,唯一的循环承载 dep 链是通过 RCX,因此该链可以向前运行。这就是为什么地址可以提前准备好,而加载/存储微指令仍然是端口 2/3 吞吐量的瓶颈。 (2认同)