#include #include #include #include #include #include #include #include "ProcessDist.h" //--------------PRIVATE DECLARATIONS---------------// void node_dist(Spawn_data spawn_data, int **qty, int *used_nodes); void spread_dist(Spawn_data spawn_data, int *used_nodes, int *procs); void compact_dist(Spawn_data spawn_data, int *used_nodes, int *procs); void generate_info_string(char *nodelist, int *procs_array, size_t nodes, MPI_Info *info); void fill_str_hosts(char *nodelist, int *qty, size_t used_nodes, char **host_str); //--------------------------------SLURM USAGE-------------------------------------// #if USE_MAL_SLURM #include void generate_info_string_slurm(char *nodelist, int *procs_array, size_t nodes, MPI_Info *info); void fill_str_hosts_slurm(char *nodelist, int *qty, size_t used_nodes, char **hostfile_str); //@deprecated functions void generate_info_hostfile_slurm(char *nodelist, int *procs_array, int nodes, MPI_Info *info); void fill_hostfile_slurm(char *nodelist, int ptr, int *qty, int used_nodes); #endif //--------------------------------SLURM USAGE-------------------------------------// int write_str_node(char **hostfile_str, size_t len_og, size_t qty, char *node_name); //@deprecated functions int create_hostfile(char **file_name); int write_hostfile_node(int ptr, int qty, char *node_name); //--------------PUBLIC FUNCTIONS---------------// /* * Configura la creacion de un nuevo grupo de procesos, reservando la memoria * para una llamada a MPI_Comm_spawn, obteniendo una distribucion fisica * para los procesos y creando un fichero hostfile. * * OUT parameters --> * info_spawn: Objeto MPI_Info en el que se indica el mappeado * a usar al crear los procesos. */ void processes_dist(Spawn_data spawn_data, MPI_Info *info_spawn) { int used_nodes=0; int *procs_array; // GET NEW DISTRIBUTION node_dist(spawn_data, &procs_array, &used_nodes); #if USE_MAL_SLURM switch(spawn_data.mapping_fill_method) { case MALL_DIST_STRING: generate_info_string_slurm(mall->nodelist, procs_array, used_nodes, info_spawn); break; case MALL_DIST_HOSTFILE: generate_info_hostfile_slurm(mall->nodelist, procs_array, used_nodes, info_spawn); break; } free(procs_array); #else generate_info_string(mall->nodelist, procs_array, used_nodes, info_spawn); #endif } //--------------PRIVATE FUNCTIONS---------------// //-----------------DISTRIBUTION-----------------// /* * Obtiene la distribucion fisica del grupo de procesos a crear, devolviendo * cuantos nodos se van a utilizar y la cantidad de procesos que alojara cada * nodo. * * Se permiten dos tipos de distribuciones fisicas segun el valor de "spawn_dist": * * COMM_PHY_NODES (1): Orientada a equilibrar el numero de procesos entre * todos los nodos disponibles. * COMM_PHY_CPU (2): Orientada a completar la capacidad de un nodo antes de * ocupar otro nodo. */ void node_dist(Spawn_data spawn_data, int **qty, int *used_nodes) { int i, *procs; procs = calloc(mall->num_nodes, sizeof(int)); // Numero de procesos por nodo /* GET NEW DISTRIBUTION */ switch(mall_conf->spawn_dist) { case MALL_DIST_SPREAD: // DIST NODES @deprecated spread_dist(spawn_data, used_nodes, procs); break; case MALL_DIST_COMPACT: // DIST CPUs compact_dist(spawn_data, used_nodes, procs); break; } //Copy results to output vector qty *qty = calloc(*used_nodes, sizeof(int)); // Numero de procesos por nodo for(i=0; i< *used_nodes; i++) { (*qty)[i] = procs[i]; } free(procs); } /* * Distribucion basada en equilibrar el numero de procesos en cada nodo * para que todos los nodos tengan el mismo numero. Devuelve el total de * nodos utilizados y el numero de procesos a crear en cada nodo. * * FIXME Tener en cuenta procesos ya creados (already_created) */ void spread_dist(Spawn_data spawn_data, int *used_nodes, int *procs) { int i, tamBl, remainder; *used_nodes = mall->num_nodes; tamBl = spawn_data.target_qty / mall->num_nodes; remainder = spawn_data.target_qty % mall->num_nodes; for(i=0; inum_nodes; i++) { procs[i] = tamBl; } } /* * Distribucion basada en llenar un nodo de procesos antes de pasar al * siguiente nodo. Devuelve el total de nodos utilizados y el numero * de procesos a crear en cada nodo. * * Tiene en cuenta los procesos ya existentes para el mappeado de * los procesos a crear. */ void compact_dist(Spawn_data spawn_data, int *used_nodes, int *procs) { int i, asigCores; int tamBl, remainder; tamBl = mall->num_cpus / mall->num_nodes; asigCores = spawn_data.already_created; i = *used_nodes = spawn_data.already_created / tamBl; remainder = spawn_data.already_created % tamBl; //FIXME REFACTOR Que pasa si los nodos 1 y 2 tienen espacios libres //First nodes could already have existing procs //Start from the first with free spaces if (remainder) { procs[i] = tamBl - remainder; asigCores += procs[i]; i = (i+1) % mall->num_nodes; (*used_nodes)++; } //Assign tamBl to each node while(asigCores+tamBl <= spawn_data.target_qty) { asigCores += tamBl; procs[i] += tamBl; i = (i+1) % mall->num_nodes; (*used_nodes)++; } //Last node could have less procs than tamBl if(asigCores < spawn_data.target_qty) { procs[i] += spawn_data.target_qty - asigCores; (*used_nodes)++; } if(*used_nodes > mall->num_nodes) *used_nodes = mall->num_nodes; //FIXME Si ocurre esto no es un error? } //--------------PRIVATE FUNCTIONS---------------// //-------------------INFO SET-------------------// /* * Crea y devuelve un objeto MPI_Info con un par hosts/mapping * en el que se indica el mappeado a utilizar en los nuevos * procesos. * * */ void generate_info_string(char *nodelist, int *procs_array, size_t nodes, MPI_Info *info){ char *host_str; fill_str_hosts(nodelist, procs_array, nodes, &host_str); // SET MAPPING MPI_Info_create(info); MPI_Info_set(*info, "hosts", mall->nodelist); free(host_str); } /* * Crea y devuelve una cadena para ser utilizada por la llave "hosts" * al crear procesos e indicar donde tienen que ser creados. */ void fill_str_hosts(char *nodelist, int *qty, size_t used_nodes, char **host_str) { char *host, *aux, *token; size_t i=0,len=0; aux = (char *) malloc((strlen(nodelist)+1) * sizeof(char)); strcpy(aux, nodelist); token = strtok(aux, ","); while (token != NULL && i < used_nodes) { host = strdup(token); if (qty[i] != 0) { len = write_str_node(host_str, len, qty[i], host); } i++; free(host); token = strtok(NULL, ","); } free(aux); } /* * Añade en una cadena "qty" entradas de "node_name". * Realiza la reserva de memoria y la realoja si es necesario. */ int write_str_node(char **hostfile_str, size_t len_og, size_t qty, char *node_name) { int err; char *ocurrence; size_t i, len, len_node; len_node = strlen(node_name) + 1; // Str length + ',' len = qty * len_node; // Number of times the node is used if(len_og == 0) { // Memoria no reservada *hostfile_str = (char *) malloc((len+1) * sizeof(char)); } else { // Cadena ya tiene datos *hostfile_str = (char *) realloc(*hostfile_str, (len_og + len + 1) * sizeof(char)); } if(hostfile_str == NULL) return -1; // No ha sido posible alojar la memoria ocurrence = (char *) malloc((len_node+1) * sizeof(char)); if(ocurrence == NULL) return -2; // No ha sido posible alojar la memoria err = snprintf(ocurrence, len_node+1, ",%s", node_name); if(err < 0) return -3; // No ha sido posible escribir sobre la variable auxiliar i=0; if(len_og == 0) { // Si se inicializa, la primera es una copia i++; strcpy(*hostfile_str, node_name); } for(; i