Estoy probando el ancho de banda de la memoria en una computadora de escritorio y un servidor.
Sklyake desktop 4 cores/8 hardware threads Skylake server Xeon 8168 dual socket 48 cores (24 per socket) / 96 hardware threadsEl ancho de banda pico del sistema es
Peak bandwidth desktop = 2-channels*8*2400 = 38.4 GB/s Peak bandwidth server = 6-channels*2-sockets*8*2666 = 255.94 GB/sEstoy usando mi propia función de tríada de STREAM para medir el ancho de banda (código completo más adelante)
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]; }Aquí están los resultados que obtengo
Bandwidth (GB/s) threads Desktop Server 1 28 16 2(24) 29 146 4(48) 25 177 8(96) 24 189Para 1 hilo, no entiendo por qué el escritorio es mucho más rápido que el servidor. Según esta respuesta https://stackoverflow.com/a/18159503/2542702 SSE es suficiente para obtener el ancho de banda completo de un sistema de doble canal. Eso es lo que observo en el escritorio. Dos subprocesos solo ayudan un poco y 4 y 8 subprocesos dan un resultado peor. Pero en el servidor, el ancho de banda de un solo subproceso es mucho menor. ¿Por qué es esto?
En el servidor obtengo los mejores resultados usando 96 subprocesos. Hubiera pensado que estaría saturado con muchos menos hilos. ¿Por qué se necesitan tantos subprocesos para saturar el ancho de banda en el servidor? Hay un gran margen de error en mis resultados y no incluyo una estimación de error. Tomé el mejor resultado de varias carreras.
El código
//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.2fs, %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.2fs, %8.2f GB/s, bw/peak_bw %8.2f %%\n", mem, dtime, bw, 100*bw/peak_bw); }