programing

많은 채널 x86 시스템의 메모리 대역폭

sourcetip 2022. 7. 16. 08:48
반응형

많은 채널 x86 시스템의 메모리 대역폭

데스크톱과 서버에서 메모리 대역폭을 테스트하고 있습니다.

Sklyake desktop 4 cores/8 hardware threads
Skylake server Xeon 8168 dual socket 48 cores (24 per socket) / 96 hardware threads

시스템의 최대 대역폭은 다음과 같습니다.

Peak bandwidth desktop = 2-channels*8*2400 = 38.4 GB/s
Peak bandwidth server  = 6-channels*2-sockets*8*2666 = 255.94 GB/s

STREAM의 자체 트라이어드 함수를 사용하여 대역폭을 측정하고 있습니다(나중에 전체 코드).

void triad(double *a, double *b, double *c, double scalar, size_t n) {
  #pragma omp parallel for
  for(int i=0; i<n; i++) a[i] = b[i] + scalar*c[i];
}

다음은 제가 얻은 결과입니다.

         Bandwidth (GB/s)
threads  Desktop  Server         
1             28      16
2(24)         29     146
4(48)         25     177
8(96)         24     189 

1 스레드에서는 데스크톱이 서버보다 훨씬 빠른 이유를 이해할 수 없습니다.이 답변에 따르면 https://stackoverflow.com/a/18159503/2542702 SSE는 듀얼 채널시스템의 모든 대역폭을 취득하기에 충분합니다.이것이 제가 데스크탑에서 관찰한 것입니다.2개의 스레드는 약간 도움이 되고 4개의 스레드와 8개의 스레드는 더 나쁜 결과를 가져옵니다.그러나 서버에서는 단일 스레드 대역폭이 훨씬 적습니다.왜 그럴까?

서버에서는 96개의 스레드를 사용하여 최적의 결과를 얻을 수 있습니다.나는 그것이 훨씬 적은 실들로 포화될 것이라고 생각했을 것이다.서버의 대역폭을 포화시키는 데 많은 스레드가 필요한 이유는 무엇입니까?제 결과에는 큰 오차가 있고 오차 추정치는 포함하지 않습니다.나는 몇 번의 실점으로 최고의 결과를 얻었다.

코드

//gcc -O3 -march=native triad.c -fopenmp
//gcc -O3 -march=skylake-avx512 -mprefer-vector-width=512 triad.c -fopenmp
#include <stdio.h>
#include <omp.h>
#include <x86intrin.h>

void triad_init(double *a, double *b, double *c, double k, size_t n) {
  #pragma omp parallel for
  for(size_t i=0; i<n; i++) a[i] = k, b[i] = k, c[i] = k;
}

void triad(double *a, double *b, double *c, double scalar, size_t n) {
  #pragma omp parallel for
  for(size_t i=0; i<n; i++) a[i] = b[i] + scalar*c[i];
}

void triad_stream(double *a, double *b, double *c, double scalar, size_t n) {
#if defined ( __AVX512F__ ) || defined ( __AVX512__ )
  __m512d scalarv = _mm512_set1_pd(scalar);
  #pragma omp parallel for
  for(size_t i=0; i<n/8; i++) {
    __m512d bv = _mm512_load_pd(&b[8*i]), cv = _mm512_load_pd(&c[8*i]);
    _mm512_stream_pd(&a[8*i], _mm512_add_pd(bv, _mm512_mul_pd(scalarv, cv)));
  }        
#else
  __m256d scalarv = _mm256_set1_pd(scalar);
  #pragma omp parallel for
  for(size_t i=0; i<n/4; i++) {
    __m256d bv = _mm256_load_pd(&b[4*i]), cv = _mm256_load_pd(&c[4*i]);
    _mm256_stream_pd(&a[4*i], _mm256_add_pd(bv, _mm256_mul_pd(scalarv, cv)));
  }        
#endif
}

int main(void) {
  size_t n = 1LL << 31LL; 
  double *a = _mm_malloc(sizeof *a * n, 64), *b = _mm_malloc(sizeof *b * n, 64), *c = _mm_malloc(sizeof *c * n, 64);
  //double peak_bw = 2*8*2400*1E-3; // 2-channels*8-bits/byte*2400MHz
  double peak_bw = 2*6*8*2666*1E-3; // 2-sockets*6-channels*8-bits/byte*2666MHz
  double dtime, mem, bw;
  printf("peak bandwidth %.2f GB/s\n", peak_bw);

  triad_init(a, b, c, 3.14159, n);
  dtime = -omp_get_wtime();
  triad(a, b, c, 3.14159, n);  
  dtime += omp_get_wtime();
  mem = 4*sizeof(double)*n*1E-9, bw = mem/dtime;
  printf("triad:       %3.2f GB, %3.2f s, %8.2f GB/s, bw/peak_bw %8.2f %%\n", mem, dtime, bw, 100*bw/peak_bw);

  triad_init(a, b, c, 3.14159, n);
  dtime = -omp_get_wtime();
  triad_stream(a, b, c, 3.14159, n);  
  dtime += omp_get_wtime();
  mem = 3*sizeof(double)*n*1E-9, bw = mem/dtime;
  printf("triads:      %3.2f GB, %3.2f s, %8.2f GB/s, bw/peak_bw %8.2f %%\n", mem, dtime, bw, 100*bw/peak_bw);
}

언급URL : https://stackoverflow.com/questions/56803987/memory-bandwidth-for-many-channels-x86-systems

반응형