Computación de altas prestaciones Computación híbrida y heterogénea Domingo Giménez Dpto. de Informática y Sistemas Universidad de Murcia http://dis.um.es/~domingo 1 Nociones básicas de 2 Programación Híbrida OpenMP MPI Paralelismo de grano fino Eficiencia en SMP Códigos secuencial y paralelo similares Herramientas de desarrollo y paralelización Permite asignación en tiempo de ejecución La asignación de memoria puede reducir las prestaciones Paralelismo de grado grueso Más portable Código paralelo muy diferente del secuencial Desarrollo y depuración más difícil Asignación estática de procesos Memorias locales, que facilitan eficiencia Nociones básicas de 3 Programación Híbrida Ventajas de la Programación Híbrida ● ● ● ● ● ● Para mejorar la escalabilidad Cuando muchas tareas producen desbalanceo Aplicaciones que combinan paralelismo de grano grueso y fino Reducción del tiempo de desarrollo de código Cuando el número de procesos MPI es fijo En caso de mezcla de paralelismo funcional y de datos Nociones básicas de 4 Programación Híbrida La gran mayoría son sistemas híbridos Nociones básicas de Programación Híbrida Modelos MPI+OpenMP OpenMP usado para paralelización de bucles OpenMP+MPI Threads no seguros Procesos MPI y OpenMP en modelo SPMD Reduce el coste de las comunicaciones 5 6 Ejemplos básicos int main(int argc, char *argv[]) { int nthreads,nprocs,idpro,idthr; int namelen; char processor_name[MPI_MAX_PROCESSOR_NAME]; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&idpro); MPI_Get_processor_name(processor_name,&namelen); #pragma omp parallel private(idthr) firstprivate(idpro,processor_name) { idthr = omp_get_thread_num(); printf("... thread %d, proceso %d procesador %s\n",idthr,idpro,processor_name); if (idthr == 0) { nthreads = omp_get_num_threads(); printf(" proceso %d, threads %d, procesos %d\n",idpro,nthreads,nprocs); } } MPI_Finalize(); } 7 Ejemplos básicos int main (int argc,char **argv) { //Variables int i,load,begin,end,*a,n,l,u,x,keepon=1,position=­1,thread=­1; int nthreads,nprocs,idpro,idthr; int namelen; char processor_name[MPI_MAX_PROCESSOR_NAME]; MPI_Status estado; //Iniciación MPI MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&idpro); MPI_Get_processor_name(processor_name,&namelen); 8 Ejemplos básicos if(idpro==0) { //Entrada y envío de datos printf("De el numero de datos del array: "); scanf("%d",&n); MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); a=(int *) malloc(sizeof(double)*n); printf("De los valores minimo y maximo: "); scanf("%d %d",&l,&u); generar_enteros(a,n,l,u); escribir_enteros(a,n); printf("De el numero a buscar: "); scanf("%d",&x); MPI_Bcast(&x, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); for(i=1;i<nprocs;i++) MPI_Send(&a[i*n/nprocs],n/nprocs,MPI_INT,i,ENVIO_INICIAL, MPI_COMM_WORLD); } else { //Recepción de datos MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Bcast(&x, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); a=(int *) malloc(sizeof(int)*n/nprocs); MPI_Recv(a,n/nprocs,MPI_INT,0,ENVIO_INICIAL,MPI_COMM_WORLD, &estado); } 9 Ejemplos básicos //Puesta en marcha de los threads #pragma omp parallel private(idthr,i,load,begin,end) { #pragma omp master nthreads = omp_get_num_threads(); idthr = omp_get_thread_num(); load = n/nprocs/nthreads; begin = idthr*load; end = begin+load; for (i = begin; ((i<end) && keepon); i++) { if (a[i] == x) { keepon = 0; position = i; thread=idthr; } #pragma omp flush(keepon,position) } } 10 Ejemplos básicos if(idpro==0) { //Recibir datos if(position!=­1) printf("Encontrado en la posicion %di, por el thread %d de %d, del proceso %d\n",position,thread,nthreads,idpro); for(i=1;i<nprocs;i++){ MPI_Recv(&position,1,MPI_INT,i,ENVIO_FINAL,MPI_COMM_WORLD,&estado); MPI_Recv(&thread,1,MPI_INT,i,ENVIO_FINAL_THR,MPI_COMM_WORLD,&estado); MPI_Recv(&nthreads,1,MPI_INT,i,ENVIO_FINAL_NTHR,MPI_COMM_WORLD, &estado); if(position!=­1) printf("Encontrado en la posicion %d,por el tread %d de %d, del proceso %d\n",position+i*n/nprocs,thread,nthreads,i); } } else { //Enviar datos MPI_Send(&position,1,MPI_INT,0,ENVIO_FINAL,MPI_COMM_WORLD); MPI_Send(&thread,1,MPI_INT,0,ENVIO_FINAL_THR,MPI_COMM_WORLD); MPI_Send(&nthreads,1,MPI_INT,0,ENVIO_FINAL_NTHR,MPI_COMM_WORLD); } MPI_Finalize(); } 11 Programación heterogénea Algoritmos HoHe: Un único proceso por procesador Con un volumen heterogéneo de datos en cada proceso Inconveniente: reprogramar los algoritmos existentes para sistemas homogéneos b0 b1 b2 b0 b1 b2 b0 b b2 b0 1 12 Programación heterogénea Algoritmos HeHo: Un número distinto de procesos por procesador en función de las velocidades relativas de los procesadores. Se decide al lanzar la ejecución (en MPI usando el fichero de máquinas) Con un volumen homogéneo de datos en cada proceso Ventaja: no es necesario reprogramar los algoritmos existentes para sistemas homogéneos Inconveniente: necesario obtener un método automático y eficiente para mapear los procesos a los procesadores.