L2 HW 프리 페 처가 정말 도움이됩니까?


10

나는에있어 위스키 호수 i7-8565U 와 (두 번 더 L2 캐시 크기보다) 데이터의 512 킬로바이트를 복사 카운터 및 시간을 반환 한 분석 및 L2 HW 프리 페처의 작품에 대하여 직면 한 몇 가지 오해.

에서 인텔 매뉴얼 4 권 MSR MSR이 0x1A4비트 0 (비활성화 1) L2 HW 프리 페처를 controlloing위한의.


다음 벤치 마크를 고려하십시오.

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
    ret

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++;
    }
}

컴파일 된 2 회 실행 고려 main.c

I .

MSR:

$ sudo rdmsr -p 0 0x1A4
0

Run:

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

 Performance counter stats for './bin':

    10486164071      L1-dcache-loads                                               (12,13%)
    10461354384      L1-dcache-load-misses     #   99,76% of all L1-dcache hits    (12,05%)
    10481930413      L1-dcache-stores                                              (12,05%)
    10461136686      l1d.replacement                                               (12,12%)
    31466394422      l1d_pend_miss.fb_full                                         (12,11%)
   211853643294      l1d_pend_miss.pending                                         (12,09%)
     1759204317      LLC-loads                                                     (12,16%)
            31007      LLC-load-misses           #    0,00% of all LL-cache hits     (12,16%)
     3154901630      LLC-stores                                                    (6,19%)
    15867315545      l2_rqsts.all_pf                                               (9,22%)
                 0      sw_prefetch_access.t1_t2                                      (12,22%)
         1393306      l2_lines_out.useless_hwpf                                     (12,16%)
     3549170919      l2_rqsts.pf_hit                                               (12,09%)
    12356247643      l2_rqsts.pf_miss                                              (12,06%)
                 0      load_hit_pre.sw_pf                                            (12,09%)
     3159712695      l2_rqsts.rfo_hit                                              (12,06%)
     1207642335      l2_rqsts.rfo_miss                                             (12,02%)
     4366526618      l2_rqsts.all_rfo                                              (12,06%)
     5240013774      offcore_requests.all_data_rd                                     (12,06%)
    19936657118      offcore_requests.all_requests                                     (12,09%)
     1761660763      offcore_response.demand_data_rd.any_response                                     (12,12%)
       287044397      bus-cycles                                                    (12,15%)
    36816767779      resource_stalls.any                                           (12,15%)
    36553997653      resource_stalls.sb                                            (12,15%)
    38035066210      uops_retired.stall_cycles                                     (12,12%)
    24766225119      uops_executed.stall_cycles                                     (12,09%)
    40478455041      uops_issued.stall_cycles                                      (12,05%)
    24497256548      cycle_activity.stalls_l1d_miss                                     (12,02%)
    12611038018      cycle_activity.stalls_l2_miss                                     (12,09%)
        10228869      cycle_activity.stalls_l3_miss                                     (12,12%)
    24707614483      cycle_activity.stalls_mem_any                                     (12,22%)
    24776110104      cycle_activity.stalls_total                                     (12,22%)
    48914478241      cycles                                                        (12,19%)

      12,155774555 seconds time elapsed

      11,984577000 seconds user
       0,015984000 seconds sys

II.

MSR:

$ sudo rdmsr -p 0 0x1A4
1

Run:

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

 Performance counter stats for './bin':

    10508027832      L1-dcache-loads                                               (12,05%)
    10463643206      L1-dcache-load-misses     #   99,58% of all L1-dcache hits    (12,09%)
    10481296605      L1-dcache-stores                                              (12,12%)
    10444854468      l1d.replacement                                               (12,15%)
    29287445744      l1d_pend_miss.fb_full                                         (12,17%)
   205569630707      l1d_pend_miss.pending                                         (12,17%)
     5103444329      LLC-loads                                                     (12,17%)
            33406      LLC-load-misses           #    0,00% of all LL-cache hits     (12,17%)
     9567917742      LLC-stores                                                    (6,08%)
     1157237980      l2_rqsts.all_pf                                               (9,12%)
                 0      sw_prefetch_access.t1_t2                                      (12,17%)
           301471      l2_lines_out.useless_hwpf                                     (12,17%)
       218528985      l2_rqsts.pf_hit                                               (12,17%)
       938735722      l2_rqsts.pf_miss                                              (12,17%)
                 0      load_hit_pre.sw_pf                                            (12,17%)
         4096281      l2_rqsts.rfo_hit                                              (12,17%)
     4972640931      l2_rqsts.rfo_miss                                             (12,17%)
     4976006805      l2_rqsts.all_rfo                                              (12,17%)
     5175544191      offcore_requests.all_data_rd                                     (12,17%)
    15772124082      offcore_requests.all_requests                                     (12,17%)
     5120635892      offcore_response.demand_data_rd.any_response                                     (12,17%)
       292980395      bus-cycles                                                    (12,17%)
    37592020151      resource_stalls.any                                           (12,14%)
    37317091982      resource_stalls.sb                                            (12,11%)
    38121826730      uops_retired.stall_cycles                                     (12,08%)
    25430699605      uops_executed.stall_cycles                                     (12,04%)
    41416190037      uops_issued.stall_cycles                                      (12,04%)
    25326579070      cycle_activity.stalls_l1d_miss                                     (12,04%)
    25019148253      cycle_activity.stalls_l2_miss                                     (12,03%)
         7384770      cycle_activity.stalls_l3_miss                                     (12,03%)
    25442709033      cycle_activity.stalls_mem_any                                     (12,03%)
    25406897956      cycle_activity.stalls_total                                     (12,03%)
    49877044086      cycles                                                        (12,03%)

      12,231406658 seconds time elapsed

      12,226386000 seconds user
       0,004000000 seconds sys

나는 카운터를 보았다 :

12 611 038 018 cycle_activity.stalls_l2_miss v / s
25 019 148 253 cycle_activity.stalls_l2_miss

MSR 비활성화 L2 HW 프리 페 처가 적용되고 있음을 나타냅니다. 또한 다른 l2 / LLC 관련 사항은 크게 다릅니다. 차이는 다른 실행에서 재현 할 수 있습니다 . 문제는 total time사이클과 차이가 거의 없다는 것입니다 .

48 914 478 241 cycles v / s
49 877 044 086 cycles

12,155774555 seconds time elapsed v / s
12,231406658 seconds time elapsed

질문 :
L2 미스가 다른 성능 제한에 의해 숨겨져 있습니까?
그렇다면 어떤 카운터를 이해해야하는지 제안 할 수 있습니까?


4
경험상 비 Abysmally로 구현 된 메모리 사본은 메모리에 바인딩됩니다. L1 캐시에만 도달 한 경우에도 마찬가지입니다. 메모리 액세스의 오버 헤드는 CPU가 2와 2를 더하는 데 걸리는 것보다 훨씬 높습니다. 귀하의 경우에는 AVX 명령어를 사용하여 복사 된 바이트 당 명령어의 양을 줄입니다. 데이터가있는 곳 (L1, L2, LLC, 메모리)이 있으면 관련 메모리 구성 요소의 처리량이 병목 현상이됩니다.
cmaster-monica reinstate

답변:


5

예, L2 스 트리머는 많은 시간을 정말 도움이됩니다.

memcpy는 계산 대기 시간이 숨겨져 있지 않으므로 OoO exec 리소스 (ROB 크기)가 적어도 L2 누락에서 얻은 추가로드 대기 시간을 처리 할 수 ​​있다고 생각합니다. L3에 맞는 중간 크기 작업 세트 (1MiB)를 사용하여 L3 적중을 발생시키는 데 프리 페치가 필요하지 않습니다.

그리고 유일한 지침은로드 / 저장 (및 루프 오버 헤드)이므로 OoO 창에는 훨씬 앞서서 요구되는로드가 포함됩니다.

L2 공간 프리 페처 및 L1d 프리 페 처가 여기서 도움을주는 경우 IDK.


이 가설을 테스트하기위한 예측 : 어레이를 더 크게 만들어 L3 미스를 얻으면 OoO exec가 DRAM으로가는로드 레이턴시를 감추기에 충분하지 않으면 전체 시간에 차이가있을 수 있습니다. HW 프리 페치 트리거가 더 멀리 진행될 수 있습니다.

HW의 다른 큰 장점은이 때 올 프리 페치 할 수 있습니다 귀하의 계산과 유지는 L2 히트를 얻을 수 있도록. (중간 길이이지만 루프 전달 종속성 체인이 아닌 계산을 갖는 루프에서)

요구로드와 OoO exec는 ROB 용량에 다른 압력이없는 경우 사용 가능한 (단일 스레드) 메모리 대역폭을 사용하는 한 많은 작업을 수행 할 수 있습니다.


또한 인텔 CPU에서 모든 캐시 미스의합니다 (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. 여기서는 중요하지 않습니다. L3 대역폭에서 32 바이트 저장소 및 병목 현상을 사용하면 클럭 당 1 포트 4 uop에 가깝지 않습니다.


2
@ St.Antario : 응? 말이되지 않습니다. 메모리가 부족하여 프런트 엔드 병목 현상이 없으므로 LSD는 관련이 없습니다. (uop 캐시에서 다시 가져 오는 것을 피하여 전력을 절약합니다). 그들은 은퇴 할 때까지 여전히 ROB에 공간을 차지합니다. 그들은 아니에요 중 하나 무시할 의미가 아니라.
Peter Cordes

2
배열을 더 크게 만들면 L3을 놓칠 수 있습니다. 16MiB버퍼와 10반복 으로 여러 테스트를 실행 했으며 실제로 14,186868883 secondsvs 43,731360909 seconds46,76% of all LL-cache hitsvs를 얻었을 때의 차이점을 볼 수 있습니다 99,32% of all LL-cache hits. 1 028 664 372 LLC-loads1 587 454 298 LLC-loads .
St.Antario

4
@ St.Antario : 등록 이름 변경으로! 이것은 xo와 같은 레지스터가 빈약 한 ISA에서 OoO exec의 가장 중요한 부분 중 하나입니다. Agner의 지시 표와 달리 mulss가 Haswell에서 3 주기만 소요하는 이유를 참조하십시오 . (여러 누산기가있는 FP 루프 풀기) . 그리고 BTW, 일반적으로로드 / 스토어로드 / 스토어가 아닌 2 개의로드를 수행 한 다음 2 개의 스토어를 수행하려고합니다. 이후로드 (HW가 이전 저장소와 겹치는 것으로 감지해야 함)가 멀리 떨어져 있기 때문에 4k 앨리어싱 스톨을 피하거나 완화 할 수있는 가능성이 높아집니다.
Peter Cordes

2
@ St.Antario : 물론입니다. Agner Fog의 최적화 가이드는 레지스터 이름을 바꾸는 OoO exec에 대해서도 설명합니다. BTW, 레지스터 이름 변경은 WAW 위험을 피하고 진정한 종속성 (RAW) 만 남습니다. 따라서 이전로드가 동일한 아키텍처 레지스터 작성 을 완료 할 때까지 기다리지 않고도로드가 순서없이 완료 될 수 있습니다 . 그렇습니다. 루프로 운반되는 유일한 뎁 체인은 RCX를 통해서만 가능하므로 체인을 앞당길 수 있습니다. 따라서로드 / 스토어 uops는 여전히 포트 2/3 처리량에 병목 현상이 발생하는 동안 주소를 조기에 준비 할 수 있습니다.
Peter Cordes

3
프리 페칭이 L3의 memcpy에 도움이되지 않은 것에 놀랐습니다. 이 경우 10/12 LFB가 "충분한"것 같습니다. 그래도 이상한 것 같습니다 : 제한 요소는 무엇입니까? 핵심-> L2 시간은 L2-> L3 시간보다 작아야하므로 내 정신 모델에서 두 번째 다리에 대한 더 많은 버퍼 (더 많은 총 점유)를 갖는 것이 도움이됩니다.
BeeOnRope

3

예, L2 HW 프리 페처는 매우 유용합니다!

예를 들어, 실행 내 컴퓨터 (i7-6700HQ)에 대한 결과를 검색 할 tinymembench을 . 결과의 첫 번째 열은 모든 프리 페 처가 켜져 있고 두 번째 결과 열은 L2 스트리 머가 꺼져있는 상태입니다 (그러나 다른 모든 프리 페처는 여전히 켜져 있음).

이 테스트는 내 컴퓨터의 L3보다 훨씬 큰 32 개의 MiB 소스 및 대상 버퍼를 사용하므로 대부분 DRAM에 대한 누락을 테스트합니다.

==========================================================================
== Memory bandwidth tests                                               ==
==                                                                      ==
== Note 1: 1MB = 1000000 bytes                                          ==
== Note 2: Results for 'copy' tests show how many bytes can be          ==
==         copied per second (adding together read and writen           ==
==         bytes would have provided twice higher numbers)              ==
== Note 3: 2-pass copy means that we are using a small temporary buffer ==
==         to first fetch data into it, and only then write it to the   ==
==         destination (source -> L1 cache, L1 cache -> destination)    ==
== Note 4: If sample standard deviation exceeds 0.1%, it is shown in    ==
==         brackets                                                     ==
==========================================================================

                                                       L2 streamer ON            OFF
 C copy backwards                                     :   7962.4 MB/s    4430.5 MB/s
 C copy backwards (32 byte blocks)                    :   7993.5 MB/s    4467.0 MB/s
 C copy backwards (64 byte blocks)                    :   7989.9 MB/s    4438.0 MB/s
 C copy                                               :   8503.1 MB/s    4466.6 MB/s
 C copy prefetched (32 bytes step)                    :   8729.2 MB/s    4958.4 MB/s
 C copy prefetched (64 bytes step)                    :   8730.7 MB/s    4958.4 MB/s
 C 2-pass copy                                        :   6171.2 MB/s    3368.7 MB/s
 C 2-pass copy prefetched (32 bytes step)             :   6193.1 MB/s    4104.2 MB/s
 C 2-pass copy prefetched (64 bytes step)             :   6198.8 MB/s    4101.6 MB/s
 C fill                                               :  13372.4 MB/s   10610.5 MB/s
 C fill (shuffle within 16 byte blocks)               :  13379.4 MB/s   10547.5 MB/s
 C fill (shuffle within 32 byte blocks)               :  13365.8 MB/s   10636.9 MB/s
 C fill (shuffle within 64 byte blocks)               :  13588.7 MB/s   10588.3 MB/s
 -
 standard memcpy                                      :  11550.7 MB/s    8216.3 MB/s
 standard memset                                      :  23188.7 MB/s   22686.8 MB/s
 -
 MOVSB copy                                           :   9458.4 MB/s    6523.7 MB/s
 MOVSD copy                                           :   9474.5 MB/s    6510.7 MB/s
 STOSB fill                                           :  23329.0 MB/s   22901.5 MB/s
 SSE2 copy                                            :   9073.1 MB/s    4970.3 MB/s
 SSE2 nontemporal copy                                :  12647.1 MB/s    7492.5 MB/s
 SSE2 copy prefetched (32 bytes step)                 :   9106.0 MB/s    5069.8 MB/s
 SSE2 copy prefetched (64 bytes step)                 :   9113.5 MB/s    5063.1 MB/s
 SSE2 nontemporal copy prefetched (32 bytes step)     :  11770.8 MB/s    7453.4 MB/s
 SSE2 nontemporal copy prefetched (64 bytes step)     :  11937.1 MB/s    7712.1 MB/s
 SSE2 2-pass copy                                     :   7092.8 MB/s    4355.2 MB/s
 SSE2 2-pass copy prefetched (32 bytes step)          :   7001.4 MB/s    4585.1 MB/s
 SSE2 2-pass copy prefetched (64 bytes step)          :   7055.1 MB/s    4557.9 MB/s
 SSE2 2-pass nontemporal copy                         :   5043.2 MB/s    3263.3 MB/s
 SSE2 fill                                            :  14087.3 MB/s   10947.1 MB/s
 SSE2 nontemporal fill                                :  33134.5 MB/s   32774.3 MB/s

이 테스트에서 L2 스 트리머를 갖는 것은 결코 느리지 않으며 종종 거의 두 배 빠릅니다.

일반적으로 결과에 다음 패턴이 나타날 수 있습니다.

  • 사본은 일반적으로 채우기보다 영향을 많이받는 것 같습니다.
  • standard memsetSTOSB fill(이 플랫폼에서 동일한 것으로 요약 ) 영향이 가장 적으며 프리 페치 된 결과가없는 것보다 몇 % 빠릅니다.
  • 표준 memcpy은 아마도 여기에서 32 바이트 AVX 명령어를 사용하는 유일한 사본이며 사본의 영향을 가장 적게받는 것입니다.

또한 다른 세 프리 페처를 켜고 끄려고 시도했지만 일반적으로이 벤치 마크에는 거의 영향을 미치지 않았습니다.


(재미있는 사실 : vmovdqa"정수"임에도 불구하고 AVX1입니까?) OP의 루프가 glibc memcpy보다 낮은 대역폭을 제공한다고 생각하십니까? 그렇기 때문에 12 개의 LFB가 L2 스트리 머가 계속 유지할 수있는 L2 <-> L3 수퍼 큐의 추가 MLP를 활용하지 않고 수요로드를 L3으로 유지하기에 충분한 이유는 무엇입니까? 아마도 테스트의 차이 일 것입니다. L3은 코어와 같은 속도로 작동해야합니다. 둘 다 쿼드 코어 Skylake 클라이언트와 동등한 마이크로 아키텍처를 가지고 있으므로 아마도 비슷한 L3 대기 시간이 있습니까?
Peter Cordes

@PeterCordes-죄송합니다. 아마도이 테스트는 32 MiB 버퍼 사이 였으므로 L3 적중이 아닌 DRAM 적중을 테스트하고 있습니다. 나는 tmb가 버퍼 크기를 출력하지만 그렇지 않다는 것을 알았다. 의도적이었습니다. OP의 512 KiB 시나리오를 정확하게 설명하려고하지는 않았지만 L2 스트리 머가 그것이 유용한 시나리오에 유용한 지 여부에 대한 헤드 라인 질문에 답하십시오. 나는 더 작은 버퍼 크기를 사용하여 결과를 어느 정도 재현 할 수 있다고 생각합니다 ( uarch-bench주석 에 언급 된 것과 비슷한 결과를 이미 보았습니다 ).
BeeOnRope

1
답변에 버퍼 크기를 추가했습니다.
BeeOnRope

1
@ St.Antario : 아니요, 문제 없습니다. 왜 이것이 문제 가 수 있을지 모르겠습니다 . AVX1과 AVX2 명령어를 믹싱하는 데 페널티가있는 것은 아닙니다. 내 의견의 요점은이 루프에는 AVX1 만 필요하지만이 답변은 AVX2 명령어 사용에 대해 언급한다는 것입니다. 인텔은 AVX2 도입과 동시에 L1d로드 / 스토어 데이터 경로를 32 바이트로 확장했습니다. 따라서 런타임 디스패치를 ​​수행하는 경우 memcpy 구현을 선택하는 방법의 일부로 AVX2의 가용성을 사용할 수 있습니다.
Peter 코드

1
프리 페처를 어떻게 해제 했습니까? 그것은되었다 software.intel.com/en-us/articles/... ? 포럼 software.intel.com/en-us/forums/intel-isa-extensions/topic/…에 따르면 일부 비트의 의미가 다릅니다.
osgx
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.