Estoy en Whisky Lake i7-8565U y analizo los contadores de rendimiento y el tiempo para copiar 512 KiB de datos (dos veces más que el tamaño de caché L2) y enfrenté algunos malentendidos con respecto al trabajo de captación previa de L2 HW.
En el Manual Intel Vol.4 MSR hay MSR, 0x1A4el bit 0 es para controlar la captura previa de L2 HW (1 para deshabilitar).
Considere el siguiente punto de referencia:
memcopy.h:
void *avx_memcpy_forward_lsls(void *restrict, const void *restrict, size_t);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
    retmain.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++;
    }
}Considere 2 ejecuciones del compilado main.c
I .
MSR:
$ sudo rdmsr -p 0 0x1A4
0Run: 
$ 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 sysII
MSR:
$ sudo rdmsr -p 0 0x1A4
1Run:
$ 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 sysMe di cuenta del mostrador:
12 611 038 018 cycle_activity.stalls_l2_miss v / s
25 019 148 253 cycle_activity.stalls_l2_miss
lo que sugiere que se está aplicando el MSR que desactiva el prefetcher L2 HW. También otras cosas relacionadas con l2 / LLC difieren significativamente. La diferencia es reproducible en diferentes ejecuciones . El problema es que casi no hay diferencia en total timey ciclos:
48 914 478 241 cycles v / s
49 877 044 086 cycles
12,155774555 seconds time elapsed v / s
12,231406658 seconds time elapsed
PREGUNTA:
 ¿Los errores de L2 están ocultos por otros limitadores de rendimiento? 
Si es así, ¿puede sugerir qué contadores mirar para entenderlo?

Respuestas:
Sí, el streamer L2 es realmente útil la mayor parte del tiempo.
memcpy no tiene ninguna latencia computacional que ocultar, así que supongo que puede permitirse el lujo de permitir que los recursos ejecutivos de OoO (tamaño ROB) manejen la latencia de carga adicional que obtienes de más fallas L2, al menos en este caso donde obtienes todos los golpes L3 de utilizando un conjunto de trabajo de tamaño mediano (1MiB) que se ajusta a L3, no es necesario realizar una captación previa para que los golpes de L3 sucedan.
Y las únicas instrucciones son cargar / almacenar (y sobrecarga de bucle), por lo que la ventana OoO incluye cargas de demanda para bastante adelante.
IDK si el prefetcher espacial L2 y el prefetcher L1d están ayudando a alguno aquí.
Predicción para probar esta hipótesis : haga que su matriz sea más grande para que pierda L3 y probablemente verá una diferencia en el tiempo general una vez que OoO exec no sea suficiente para ocultar la latencia de carga de ir a DRAM. La captación previa de HW que se activa más adelante puede ayudar a algunos.
Los otros grandes beneficios de la captación previa de HW vienen cuando puede mantenerse al día con su cálculo, por lo que obtiene golpes L2. (En un bucle que tiene cálculo con una cadena de dependencia de longitud media pero no transportada por bucle).
Las cargas de demanda y OoO exec pueden hacer mucho en cuanto al uso del ancho de banda de memoria disponible (un solo subproceso), cuando no hay otra presión sobre la capacidad de ROB.
También tenga en cuenta que en las CPU de Intel, cada pérdida de caché puede costar una reproducción de fondo (desde el RS / planificador) de uops dependientes , uno para L1d y L2 falla cuando se espera que lleguen los datos. Y después de eso, aparentemente el núcleo optimistamente espanta uops mientras espera que lleguen datos desde L3.
(Consulte https://chat.stackoverflow.com/rooms/206639/discussion-on-question-by-beeonrope-are-load-ops-deallocated-from-the-rs-when-th y las operaciones de carga se desasignan del ¿RS cuando envían, completa o en otro momento? )
No la carga de caché-miss en sí misma; en este caso serían las instrucciones de la tienda. Más específicamente, el almacenamiento de datos de la tienda para el puerto 4. Eso no importa aquí; El uso de tiendas de 32 bytes y el cuello de botella en el ancho de banda L3 significa que no estamos cerca de 1 puerto 4 uop por reloj.
fuente
16MiBbuffer e10iteraciones y de hecho obtuve14,186868883 secondsvs43,731360909 secondsy46,76% of all LL-cache hitsvs99,32% of all LL-cache hits;1 028 664 372 LLC-loadsvs1 587 454 298 LLC-loads.Sí, ¡el prefetcher L2 HW es muy útil!
Por ejemplo, encuentre los siguientes resultados en mi máquina (i7-6700HQ) que ejecuta tinymembench . La primera columna de resultados está con todos los prefetchers encendidos, la segunda columna de resultados está con el streamer L2 apagado (pero todos los otros prefetchers todavía están encendidos).
Esta prueba utiliza 32 búferes de origen y destino MiB, que son mucho más grandes que el L3 en mi máquina, por lo que probará la mayoría de las fallas en DRAM.
En estas pruebas, tener el streamer L2 nunca es más lento y suele ser casi el doble de rápido.
En general, puede observar los siguientes patrones en los resultados:
standard memsetySTOSB fill(estos se reducen a lo mismo en esta plataforma) son los menos afectados, con el resultado obtenido previamente solo un poco más rápido que sin ellos.memcpyes probablemente la única copia aquí que usa instrucciones AVX de 32 bytes, y está entre las menos afectadas, pero la captación previa sigue siendo ~ 40% más rápida que sin ella.También intenté encender y apagar los otros tres captadores previos, pero en general casi no tuvieron un efecto medible para este punto de referencia.
fuente
vmovdqacurioso : es AVX1 a pesar de ser "entero"). ¿Crees que el bucle del OP estaba dando un ancho de banda menor que la memoria glibc? Y es por eso que 12 LFB fueron suficientes para mantenerse al día con las cargas de demanda que van a L3, sin aprovechar el MLP adicional de la supercuesta L2 <-> L3 que el transmisor L2 puede mantener ocupado. Esa es probablemente la diferencia en su prueba. L3 debe funcionar a la misma velocidad que el núcleo; Ambos tienen microarquitecturas equivalentes al cliente Skylake de cuatro núcleos, ¿es probable que tengan una latencia L3 similar?uarch-benchen los comentarios).