Taller de Programación Paralela Fernando R. Rannou Departamento de Ingenierı́a Informática Universidad de Santiago de Chile July 18, 2008 MPI 2 MPI 2 Comunicadores y grupos (1) MPI 2 ■ Un comunicador se compone de: 1. un grupo de procesos colección ordenada de procesos 2. un contexto objeto definido por el sistema que identifica univocamente al comunicador ■ En MPI existe 1. intra-comunicadores: colección de procesos que se pueden comunicar mediante el envı́o y recepción de mensajes, ya sea punto-a-punto y colectivamente 2. inter-comunicadores: objetos que permiten que procesos de intra-comunicadores diferentes se comuniquen Fernando R. Rannou Diinf-USACH 3 / 24 Comunicadores y grupos (2) MPI 2 ■ Un proceso puede pertenecer a más de un grupo/comunicador ■ Para el programador, grupo y comunicador es una misma cosa. ■ Permite al programador organizar las tareas en grupos de tareas ■ Permite operaciones de comunicación colectiva sobre un subconjunto de tareas relacionadas ■ Forman la base para crear topologı́as virtuales Fernando R. Rannou Diinf-USACH 4 / 24 Creación de comunicadores MPI 2 ■ MPI permite crear nuevos comunicadores consistente de un sub-grupo de procesos ■ Las etapas son: 1. Identificar el sub-grupo de procesos 2. Crear el nuevo grupo (vacı́o) 3. Crear el nuevo comunicador Fernando R. Rannou Diinf-USACH 5 / 24 Grupos y comunicadores MPI 2 Fernando R. Rannou Diinf-USACH 6 / 24 Creación de comunicadores (cont) MPI 2 int MPI_Comm_group(MPI_Comm comm, MPI_Group *group); ■ Retorna el grupo group asociado a un comunicador comm int MPI_Group_incl(MPI_Group old_group, int new_group_size, int ranks_in_old_group[], MPI_Group *new_group); ■ Incluye los procesos con ranks ranks in old group[] del antiguo grupo, en un nuevo grupo new group int MPI_Comm_create(MPI_Comm old_comm, MPI_Group new_group, MPI_Comm *new_comm); ■ Crea un nuevo comunicador new comm con el grupo de procesos new group Fernando R. Rannou Diinf-USACH 7 / 24 Ejemplo 1 MPI 2 MPI_Group world_group, firstrow_group; MPI_Comm firstrow_comm; int *ranks; ... ranks = (int *) malloc(sizeof(int)*q); // q es largo de la fila for (i=0; i < q; i++) ranks[i] = i; MPI_Comm_group(MPI_COMM_WORLD, &world_group); MPI_Group_incl(world_group, q, ranks, &firstrow_group); MPI_Comm_create(MPI_COMM_WORLD, firstrow_group, &firstrow_comm); ... 0 1 8 5 10 9 4 2 2 1 0 3 3 6 firstrow_comm 7 11 MPI_COMM_WORLD Fernando R. Rannou Diinf-USACH 8 / 24 Ejemplo 2 MPI 2 1 2 3 ranks1 [4]={0 ,1 ,2 ,3} , ranks2 [4]={4 ,5 ,6 ,7}; MPI_Group orig_group , new_group ; MPI_Comm new_comm ; 4 5 6 7 MPI_Init (& argc ,& argv ) ; MPI_Comm_rank ( MPI_COMM_WORLD , & rank ) ; MPI_Comm_size ( MPI_COMM_WORLD , & numtasks ) ; 8 9 10 sendbuf = rank ; MPI_Comm_group ( MPI_COMM_WORLD , & orig_group ) ; 11 12 13 14 15 if ( rank < 4) MPI_Group_incl ( orig_group , 4 , ranks1 , & new_group ) ; else MPI_Group_incl ( orig_group , 4 , ranks2 , & new_group ) ; 16 17 18 MPI_Comm_create ( MPI_COMM_WORLD , new_group , & new_comm ) ; MPI_Allreduce (& sendbuf , & recvbuf , 1 , MPI_INT , MPI_SUM , new_comm ); 19 MPI_Group_rank ( new_group , & new_rank ) ; 21 printf ( " rank = % d newrank = % d recvbuf = % d \ n " , rank , new_rank , recvbuf ) ; Fernando R. Rannou 20 Diinf-USACH 9 / 24 Particionando comunicadores MPI 2 ■ El procedimiento anterior es muy tedioso, por ejemplo, en el caso que tuvieramos que crear un comunicador para cada fila de una matrix de 100 × 100 ■ Con MPI Comm split() podemos crear múltiples comunicadores int MPI_Comm_split(MPI_Comm old_comm, int split_key, int rank_key, MPI_Comm *new_comm); ■ Crea un nuevo comunicador para cada valor de split key. ■ Los procesos que invocan MPI Comm split() con la misma llave split key forman un nuevo grupo y comunicador ■ rank key define el nuevo rango en el nuevo comunicador: sean A y B dos procesos con el mismo split key: 1. si rank key de A es menor que rank key de B, entonces el rango de A será menor que el rango de B en el nuevo comunicador 2. Si ambos procesos tienen el mismo rank key, el sistema asignará rangos arbitrarios. Fernando R. Rannou Diinf-USACH 10 / 24 Ejemplo MPI Comm split() MPI 2 Supongamos que deseamos crear un comunicador para cada fila del ejemplo 1 anterior MPI_Comm row_comm; int row; ... MPI_Comm_rank(MPI_COMM_WORLD, &rank); row = rank/q; MPI_Comm_split(MPI_COMM_WORLD, row, rank, &row_comm); 0 0/0 1 2/2 8 5 3/3 0/4 1/5 2/6 3/7 10 9 4 1/1 2 3 0/8 1/9 2/10 3/11 6 7 11 MPI_COMM_WORLD Fernando R. Rannou Diinf-USACH 11 / 24 Topologı́as MPI 2 1. Una topologı́a virtual describe un mapeo/ordenamiento sobre los procesos en una forma geométrica. 2. Una topologı́a se construye a partir de los grupos y comunicadores 3. Por ejemplo, si la aplicación involucra comunicación de procesos vecinos, como lo muestra la figura, serı́a mejor crear una topologı́a ad-hoc 4. Sin embargo, la topologı́a no restringe que un proceso se comunique con otro dentro de su comunicador Fernando R. Rannou Diinf-USACH 12 / 24 Topologı́as Cartesianas y de grafos MPI 2 ■ MPI permite la definición de topologı́a de tipo Cartesiano, en la que los procesos se ordenan con una estructura regular (cı́clica o no) ■ MPI también permite topologı́as generales o de grafos como la siguiente figura ■ Nosotros sólo veremos topologı́as Cartesianas 0 1 Fernando R. Rannou Diinf-USACH 2 3 4 13 / 24 Topologı́as Cartesianas MPI 2 int MPI_Cart_create(MPI_Comm old_comm, int number_of_dims, int dim_sizes[], int wrap_around[], int reorder, MPI_comm *new_comm); ■ A partir del comunicador old comm, MPI Cart create() crea un nuevo comunicador, new comm, donde los procesos se estructuran en una grilla de number of dims dimensiones. ■ El tamaño de cada dimensión es especificada en el arreglo dim size[] ■ El arreglo wrap around[] especifica si la dimensión es lineal o circular: ◆ wrap around[i] = 1 → dimensión i es circular ◆ wrap around[i] = 0 → dimensión i es lineal ■ reorder especifica si los ranks de los procesos deben o no permanecer igual ◆ reorder = 0 → usar los mismos ranks ◆ reorder = 1 → reordenar Fernando R. Rannou Diinf-USACH 14 / 24 Ejemplo con MPI Cart create() MPI 2 MPI_comm my_2Dcomm; ... int dim_sizes[3]; dim_sizes[0] = 2; wrap_around[0] = dim_sizes[1] = 3; wrap_around[1] = dim_sizes[2] = 3; wrap_around[2] = MPI_Cart_create(MPI_COMM_WORLD, 3, ... 0; 0; 1; dim_sizes, wrap_around, 1, &my_2Dcomm); ■ Crea una grilla de 2 × 3 × 3, que es circular sólo en la dimensión 3. ■ Los nuevos ranks son asignados en orden row mayor ■ MPI Cart create() es una operación colectiva 12 6 Fernando R. Rannou Diinf-USACH 13 14 8 7 0 1 2 3 4 5 17 11 15 / 24 Coordenadas a partir del rank MPI 2 ■ Para obtener el rank en el nuevo comunicador, simplemente invocamos MPI Comm rank() ■ Si deseamos obtener las “coordenadas” del proceso (posición Cartesiana en la grilla), invocamos MPI Cart coords() int MPI_Cart_coords(MPI_Comm comm, int rank, int number_of_dims, int coordinates[]); ■ coordinates[] es una arreglo de largo number of dims tal que coordinates[i] es la posición en la dimensión i ■ Note que para obtener las coordenas, primero debemos conocer el rank ■ ¿Qué pasarı́a si las coordenas están fuera de la grilla? Fernando R. Rannou Diinf-USACH 16 / 24 Ranks a partir de coordenadas MPI 2 ■ Si deseamos saber el rank del proceso en una cierta posición en la grilla, llamamos int MPI_Cart_rank(MPI_Comm comm, int coordinates[], int *rank); ■ Tanto MPI Cart coords() como MPI Cart rank() son funciones locales. ■ No confundir MPI Comm rank() con MPI Cart rank() Fernando R. Rannou Diinf-USACH 17 / 24 MPI Cart shift() MPI 2 int MPI_Cart_shift(MPI_Comm comm, int direction, int displ, int *source, int *dest ) ■ Retorna los rangos de los procesos “vecinos” fuentes (source) y destinos (dest) que están a una distancia displ en la dirección direction ■ Si la grilla es N dimensional, entonces 0 ≤ direction < N . ■ Un valor displ = 1 indica los vecinos inmediatos ■ Si displ > 0, source es el proceso con menor rango y dest con mayor rango ■ Si displ < 0 se revierte la lógica anterior Fernando R. Rannou Diinf-USACH 18 / 24 Ejemplo MPI Cart shift() MPI 2 Fernando R. Rannou Diinf-USACH 19 / 24 Creando sub-comunicadores MPI 2 int MPI_Cart_sub(MPI_Comm cart_comm, int free_coords[], MPI_Comm sub_comm); ■ MPI Cart sub() permite crear nuevos comunicadores para subgrupos de procesos que ya pertenecen a un comunicador topologı́a grilla ■ Útil para cuando un proceso necesita realizar una operación de recucción sólo en una fila o columna, o en general en sólo una de las dimensiones ■ free coords[] es un arreglo de booleanos de largo num dim que especifica si cada dimensión es parte del nuevo comunicador, tal que free coords[i] = 0 si la dimensión i es libre o no, es decir los procesos con la misma dimensión i pertenecen al mismo comunicador ■ Se crean varios nuevos comunicadores sub comm Fernando R. Rannou Diinf-USACH 20 / 24 Ejemplo MPI Cart sub() MPI 2 ■ Ejemplo 0 (0,0) 4 1 (0,1) 2 (0,2) 5 6 (1,1) (1,2) 3 (0,3) 7 MPI_Comm my_2Dcomm, row_comm; ... 8 9 10 11 (2,0) (2,1) (2,2) (2,3) int dim_sizes[2]; dim_sizes[0] = 3; wrap_around[0] = 0; dim_sizes[1] = 4; wrap_around[1] = 1; MPI_Cart_create(MPI_COMM_WORLD, 2, dim_sizes, wrap_around, 1, &my_2Dcomm); ... int free_coords[2]; free_coords[0] = 0; free_coords[1] = 1; MPI_Cart_sub(my_2Dcomm, free_coords, row_comm); (1,0) Fernando R. Rannou Diinf-USACH (1,3) 21 / 24 Mensajes compuestos MPI 2 ■ MPI provee tres métodos para agrupar datos escalares en un mensaje: 1. El parámetro count; como el usado en las funciones punto-a-punto y colectivas para enviar arreglos de enteros o flotantes 2. Tipos de datos derivados; sistema complejo de entender 3. Empaquetamiento de datos; el más fácil Fernando R. Rannou Diinf-USACH 22 / 24 Empaquetamiento MPI 2 ■ 1 2 3 4 5 6 7 8 9 10 11 12 13 Suponga que un proceso necesita enviar un mensaje compuesto de dos flotantes (float) y un entero (int) a todos los procesos en su comunicador float * a ; float * b ; int * c ; ... char buffer [100]; int position ; if ( myrank == 0) { position = 0; MPI_Pack (a , 1 , MPI_FLOAT , buffer , 100 , & position , MPI_COMM_WORLD ) ; MPI_Pack (b , 1 , MPI_FLOAT , buffer , 100 , & position , MPI_COMM_WORLD ) ; MPI_Pack (c , 1 , MPI_INT , buffer , 100 , & position , MPI_COMM_WORLD ) ; MPI_Bcast ( buffer , 100 , MPI_PACKED , 0 , MPI_COMM_WORLD ) ; } else { ... Fernando R. Rannou Diinf-USACH 23 / 24 Desempaquetamiento MPI 2 1 2 3 4 5 6 7 8 ... else { MPI_Bcast ( buffer , 100 , MPI_PACKED , position = 0; MPI_Unpack ( buffer , 100 , & position , MPI_COMM_WORLD ) ; MPI_Unpack ( buffer , 100 , & position , MPI_COMM_WORLD ) ; MPI_Unpack ( buffer , 100 , & position , MPI_COMM_WORLD ) ; } 0 , MPI_COMM_WORLD ) ; a , 1 , MPI_FLOAT , b , 1 , MPI_FLOAT , c , 1 , MPI_INT , int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outcount, int *position, MPI_Comm comm ); int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm); Fernando R. Rannou Diinf-USACH 24 / 24