NUMA práctica

Presenter Notes

Plan de clase

  • Autoparalelización.
  • numactl, taskset.
  • Ejemplo que anda mal en OpenMP si no asignamos bien la memoria inicialmente.
    • Porque si funciona en MPI bajo shmem.
  • Todo en mendieta.
  • Tratar de recuperar ese caso de estudio raro que planteó el chico de Rosario. Había links interesantes.

Faltó

  • GOMP_CPU_AFFINITY (GNU), MP_BIND y MP_BLIST (PGI), PSC_OMP_AFFINITY_MAP (EKOPath), KMP_AFFINITY (Intel).

Nicolás Wolovick 20140513

Presenter Notes

Autoparalelización

 1 PROGRAM EASYPARALLEL
 2 
 3     PARAMETER (N = 2**28)
 4     REAL A(N)
 5     REAL B(N)
 6 c   DO I = 1,N
 7 c       B(I) = I*3.14159 + N - I
 8 c   ENDDO
 9 
10     DO I = 1,N
11         A(I) = B(I) * 3.14159
12     ENDDO
13 END

Probamos

1 $ gfortran -O3 -floop-parallelize-all -ftree-parallelize-loops=2 hpc_p123.f && perf stat -r 5 -e task-clock ./a.out
2 
3  Performance counter stats for './a.out' (5 runs):
4 
5        1092,059892 task-clock                #    1,740 CPUs utilized            ( +-  0,17% )
6 
7        0,627569616 seconds time elapsed                                          ( +-  0,39% )

Presenter Notes

Otro caso de autopar

 1 float a[N][N], b[N], c[N];
 2 
 3 int main(void) {
 4     unsigned int i = 0, j = 0;
 5     double start = 0.0;
 6 
 7     start = omp_get_wtime();
 8     for (i=0; i<N; ++i)
 9         for (j=0; j<N; ++j)
10             c[i] += a[i][j]*b[j];
11     printf("%f ", ((long)N*N*3*sizeof(float))/((1<<30)*(omp_get_wtime()-start)));
12 
13     return 0;
14 }

-ftree-parallelize-loops=n, donde n es la cantidad de hilos.

Mostrar juego entre:

-floop-parallelize-all: lo saco y tengo multicore.
-ffast-math: lo pongo y tengo vectorización.

¿No pude? hacer andar autovectorización y autoparalelización.
Sigo buscando el vellocino de oro.

Presenter Notes

Mediciones en mendieta

1 $ gcc -O3 -ftree-parallelize-loops=16 sgemv.c && perf stat -r 5 -e task-clock ./a.out
2 61.421345 61.059372 60.920014 60.466478 61.158156 
3  Performance counter stats for './a.out' (5 runs):
4 
5 767,697361 task-clock                #   14,689 CPUs utilized            ( +-  0,76% )
6 
7 0,052262059 seconds time elapsed                                          ( +-  0,31% )

En OpenMP era un one-liner, que lo pude evitar sin problema.

Notar que llega a 60 GiB/s, casi el tope del ancho de banda de estas máquinas.
(bueno en realidad no, son dos pastillas E5-2680, y cada una tiene 51.2 GiB/s)

Nota

Mirar el reporte de autoparalelización con -fdump-tree-parloops-all.

Concurso

Lograr autoparalelizar y autovectorizar sgemv.
Premio: un paquete de "Sonrisas®".

Presenter Notes

Autoparalelizador y autovectorizador

  • For Data Dependence :
    gcc -fdump-tree-all -fcheck-data-deps -fdump-tree-ckdd-all -O3 filename.c
  • For Vectorization :
    gcc -fdump-tree-all -fdump-tree-vect-all -msse4 -O3 filename.c
  • For Parallelization :
    gcc -fdump-tree-all -ftree-parallelize-loops=x -fdump-tree-parloops-all -O3 filename.c
  • Graphite Parallelization :
    gcc -fdump-tree-all -ftree-parallelize-loops=x -fdump-tree-parloops-all -floop-parallelize-all -O2 filename.c
  • For Loop Interchange :
    gcc -fdump-tree-all -floop-interchange -fdump-tree-graphite-all -O3 filename.c

Sacado de PARALLELIZATION AND VECTORIZATION IN GCC.

Presenter Notes

NUMA

Presenter Notes

NUMA en hardware

Se puede deshabilitar el interleave.

http://frankdenneman.nl/2010/12/node-interleaving-enable-or-disable/

Presenter Notes

UMA en hardware (con máquina NUMA)

Se puede habilitar el interleave.

http://frankdenneman.nl/2010/12/node-interleaving-enable-or-disable/

  • Transforma una máquina NUMA en UMA.
  • Promedio del comportamiento (es más determinística).
  • Le mete presión al interconnect.
  • Nunca activen el interleave.

Presenter Notes

NUMA en Mendieta

lstopo -p --no-io --no-bridges --no-caches --of png > mendieta-lstopo.png

lstopo -p --no-io --no-bridges --no-caches --of png > mendieta-lstopo.png

Presenter Notes

NUMA en Mendieta

 1 $ numactl --hardware
 2 available: 2 nodes (0-1)
 3 node 0 cpus: 0 1 2 3 4 5 6 7
 4 node 0 size: 16355 MB
 5 node 0 free: 15254 MB
 6 node 1 cpus: 8 9 10 11 12 13 14 15
 7 node 1 size: 16384 MB
 8 node 1 free: 15805 MB
 9 node distances:
10 node   0   1 
11   0:  10  21 
12   1:  21  10

Notar como se informa de la memoria disponible por nodo.

Presenter Notes

Experimentos con numactl

Puedo controlar cómo quiero que use la memoria respecto a nodos.

Esto es usando el sgemv.c autoparalelizado.

 1 $numactl --cpunodebind=1 --membind=1 ./a.out
 2 37.390317
 3 $ numactl --cpunodebind=0 --membind=1 ./a.out
 4 19.060755
 5 $ numactl --cpunodebind=1 --membind=0 ./a.out
 6 21.900698
 7 $ numactl --cpunodebind=0,1 --membind=0,1 ./a.out
 8 69.289990
 9 $ numactl --cpunodebind=0 --membind=0,1 ./a.out
10 33.960225
11 $ numactl --cpunodebind=0,1 --membind=0 ./a.out
12 42.920589

Presenter Notes

numactl como taskset

¡Atar procesos a cores!

sgemv.c para N=1L<<16.
Ojo, hay que compilar con -mcmodel=large. Las cuentas dicen que son ~16 GiB
(((1<<16)*(1<<16) + 2*(1<<16)) * 4.0) / (1<<30) = 16.00048828125

1 $ numactl --physcpubind=0 ./a.out
2 6.915849
3 $ numactl --physcpubind=0-1 ./a.out
4 13.575607
5 $ numactl --physcpubind=0-15 ./a.out
6 68.431601

El último es idem a:

1 $ taskset 0x0000FFFF ./a.out
2 69.925633

Presenter Notes

sgemv paralelo

 1 #define N (1L<<16)
 2 float a[N][N], b[N], c[N];
 3 
 4 int main(void) {
 5     int i = 0, j = 0;
 6     double start = 0.0;
 7 
 8     start = omp_get_wtime();
 9     #pragma omp parallel for shared(a,b,c,start) private(i,j)
10     for (i=0; i<N; ++i)
11     for (j=0; j<N; ++j)
12         c[i] += a[i][j]*b[j];
13     printf("%f ", ((long)N*N*3*sizeof(float))/((1<<30)*(omp_get_wtime()-start)));
14 
15     return 0;
16 }

Resultado en GiB/s:

1 $ gcc -O3 -mcmodel=large -fopenmp parallel_sgemv.c && ./a.out 
2 73.599593

Presenter Notes

sgemv paralelo

En medio de la ejecución (puse un pause 0 antes de salir) tenemos:

 1 $ numactl --hardware
 2 available: 2 nodes (0-1)
 3 node 0 cpus: 0 1 2 3 4 5 6 7
 4 node 0 size: 16355 MB
 5 node 0 free: 6861 MB
 6 node 1 cpus: 8 9 10 11 12 13 14 15
 7 node 1 size: 16384 MB
 8 node 1 free: 6965 MB
 9 node distances:
10 node   0   1 
11   0:  10  21 
12   1:  21  10

Ambos nodos NUMA ocupados.

Notar: que hay ~16 GiB ocupados distribuidos en los dos nodos.

Presenter Notes

Otra forma de leer consumo de memoria

Para evitar poner un getchar(), se puede usar /usr/bin/time.

Entre otras cosas muestra el consumo máximo de memoria residente

1 $ gcc -O3 -mcmodel=large -fopenmp parallel_sgemv_nonnuma.c && /usr/bin/time -f '%MkB' ./a.out
2 75.722791 
3 67114112kB

Ojo con /usr/bin/time versión 1.7, hay que dividir el resultado por 4.
O sea da bien: ~16 GiB.

Presenter Notes

sgemv init 0

 1 #define N (1L<<16)
 2 
 3 float a[N][N], b[N], c[N];
 4 
 5 int main(void) {
 6     int i = 0, j = 0;
 7     double start = 0.0;
 8 
 9     memset(a, 0, N*N*sizeof(float));
10     memset(b, 0, N*sizeof(float));
11     memset(c, 0, N*sizeof(float));
12 
13     start = omp_get_wtime();
14     #pragma omp parallel for shared(a,b,c,start) private(i,j)
15     for (i=0; i<N; ++i)
16     for (j=0; j<N; ++j)
17         c[i] += a[i][j]*b[j];
18     printf("%f ", ((long)3*N*N*sizeof(float))/((1<<30)*(omp_get_wtime()-start)));
19 
20     return 0;
21 }

Resultado en GiB/s:

1 $ gcc -O3 -mcmodel=large -fopenmp parallel_sgemv_nonnuma.c && ./a.out 
2 82.269709

Presenter Notes

sgemv init 0

En medio de la ejecución tenemos:

 1 $ numactl --hardware
 2 available: 2 nodes (0-1)
 3 node 0 cpus: 0 1 2 3 4 5 6 7
 4 node 0 size: 16355 MB
 5 node 0 free: 2245 MB
 6 node 1 cpus: 8 9 10 11 12 13 14 15
 7 node 1 size: 16384 MB
 8 node 1 free: 12172 MB
 9 node distances:
10 node   0   1 
11   0:  10  21 
12   1:  21  10

Pero tenemos lo que queremos, casi toda la memoria en el nodo 0.

¿Porqué funciona rápido?
¿Porqué?
¿Ah?

Presenter Notes

Medición de comunicación NUMA

 1 [nwolovick@mendieta Clase13_20140513]$ numastat && gcc -O3 -mcmodel=large -fopenmp parallel_sgemv.c && ./a.out && numastat
 2                            node0           node1
 3 numa_hit                89139513        91739553
 4 numa_miss                   4240           24102
 5 numa_foreign               24102            4240
 6 interleave_hit             78788           78786
 7 local_node              89080660        91631851
 8 other_node                 63093          131804
 9 67.330149
10                            node0           node1
11 numa_hit                89151580        91747783
12 numa_miss                   4240           24102
13 numa_foreign               24102            4240
14 interleave_hit             78788           78786
15 local_node              89092727        91640081
16 other_node                 63093          131804

El valor de other_node es la que nos interesa.

No hubo cambio.

Presenter Notes

Medición de comunicación NUMA

La versión non-numa-aware si registra comunicación.

 1 $ numastat && gcc -O3 -mcmodel=large -fopenmp parallel_sgemv_nonnuma.c && ./a.out && numastat
 2                            node0           node1
 3 numa_hit                89096545        91700335
 4 numa_miss                   3230           24102
 5 numa_foreign               24102            3230
 6 interleave_hit             78788           78786
 7 local_node              89037692        91592633
 8 other_node                 62083          131804
 9 81.223156
10                            node0           node1
11 numa_hit                89115401        91717115
12 numa_miss                   4240           24102
13 numa_foreign               24102            4240
14 interleave_hit             78788           78786
15 local_node              89056548        91609413
16 other_node                 63093          131804

Más indicios de que efectivamente se está sobrecargando el QPI.

Presenter Notes

¿Auto migración de páginas?

No, solo está disponible a partir de Linux-3.8.

Se puede controlar desde Linux-3.14 (commit).

Ayuda sobre el tema en RHEL7 (¡aun no salió!).

Auto NUMA Balancing.

Presenter Notes

Bibliografía

Presenter Notes

La clase que viene

"De la Playstation a la Computación Científica",
ó
"¿Porqué los gamers son nuestro máximo aliado?"

Presenter Notes