| /* |
| * Copyright © 2010-2011 INRIA. All rights reserved. |
| * Copyright © 2011 Université Bordeaux 1 |
| * Copyright © 2011 Cisco Systems, Inc. All rights reserved. |
| * See COPYING in top-level directory. |
| */ |
| |
| #include <private/autogen/config.h> |
| #include <hwloc.h> |
| #include <private/private.h> |
| #include <private/debug.h> |
| |
| #include <float.h> |
| |
| /* called during topology init */ |
| void hwloc_topology_distances_init(struct hwloc_topology *topology) |
| { |
| unsigned i; |
| for (i=0; i < HWLOC_OBJ_TYPE_MAX; i++) { |
| /* no distances yet */ |
| topology->os_distances[i].nbobjs = 0; |
| topology->os_distances[i].objs = NULL; |
| topology->os_distances[i].indexes = NULL; |
| topology->os_distances[i].distances = NULL; |
| } |
| } |
| |
| /* called when reloading a topology. |
| * keep initial parameters (from set_distances and environment), |
| * but drop what was generated during previous load(). |
| */ |
| void hwloc_topology_distances_clear(struct hwloc_topology *topology) |
| { |
| unsigned i; |
| for (i=0; i < HWLOC_OBJ_TYPE_MAX; i++) { |
| /* remove final distance matrices, but keep physically-ordered ones */ |
| free(topology->os_distances[i].objs); |
| topology->os_distances[i].objs = NULL; |
| } |
| } |
| |
| /* called during topology destroy */ |
| void hwloc_topology_distances_destroy(struct hwloc_topology *topology) |
| { |
| unsigned i; |
| for (i=0; i < HWLOC_OBJ_TYPE_MAX; i++) { |
| /* remove final distance matrics AND physically-ordered ones */ |
| free(topology->os_distances[i].indexes); |
| topology->os_distances[i].indexes = NULL; |
| free(topology->os_distances[i].objs); |
| topology->os_distances[i].objs = NULL; |
| free(topology->os_distances[i].distances); |
| topology->os_distances[i].distances = NULL; |
| } |
| } |
| |
| /* insert a distance matrix in the topology. |
| * the caller gives us those pointers, we take care of freeing them later and so on. |
| */ |
| void hwloc_topology__set_distance_matrix(hwloc_topology_t __hwloc_restrict topology, hwloc_obj_type_t type, |
| unsigned nbobjs, unsigned *indexes, hwloc_obj_t *objs, float *distances) |
| { |
| free(topology->os_distances[type].indexes); |
| free(topology->os_distances[type].objs); |
| free(topology->os_distances[type].distances); |
| topology->os_distances[type].nbobjs = nbobjs; |
| topology->os_distances[type].indexes = indexes; |
| topology->os_distances[type].objs = objs; |
| topology->os_distances[type].distances = distances; |
| } |
| |
| /* make sure a user-given distance matrix is sane */ |
| static int hwloc_topology__check_distance_matrix(hwloc_topology_t __hwloc_restrict topology __hwloc_attribute_unused, hwloc_obj_type_t type __hwloc_attribute_unused, |
| unsigned nbobjs, unsigned *indexes, hwloc_obj_t *objs __hwloc_attribute_unused, float *distances __hwloc_attribute_unused) |
| { |
| unsigned i,j; |
| /* make sure we don't have the same index twice */ |
| for(i=0; i<nbobjs; i++) |
| for(j=i+1; j<nbobjs; j++) |
| if (indexes[i] == indexes[j]) { |
| errno = EINVAL; |
| return -1; |
| } |
| return 0; |
| } |
| |
| static hwloc_obj_t hwloc_find_obj_by_type_and_os_index(hwloc_obj_t root, hwloc_obj_type_t type, unsigned os_index) |
| { |
| hwloc_obj_t child; |
| if (root->type == type && root->os_index == os_index) |
| return root; |
| child = root->first_child; |
| while (child) { |
| hwloc_obj_t found = hwloc_find_obj_by_type_and_os_index(child, type, os_index); |
| if (found) |
| return found; |
| child = child->next_sibling; |
| } |
| return NULL; |
| } |
| |
| static void hwloc_get_type_distances_from_string(struct hwloc_topology *topology, |
| hwloc_obj_type_t type, char *string) |
| { |
| /* the string format is: "index[0],...,index[N-1]:distance[0],...,distance[N*N-1]" |
| * or "index[0],...,index[N-1]:X*Y" or "index[0],...,index[N-1]:X*Y*Z" |
| */ |
| char *tmp = string, *next; |
| unsigned *indexes; |
| float *distances; |
| unsigned nbobjs = 0, i, j, x, y, z; |
| |
| /* count indexes */ |
| while (1) { |
| size_t size = strspn(tmp, "0123456789"); |
| if (tmp[size] != ',') { |
| /* last element */ |
| tmp += size; |
| nbobjs++; |
| break; |
| } |
| /* another index */ |
| tmp += size+1; |
| nbobjs++; |
| } |
| |
| if (*tmp != ':') { |
| fprintf(stderr, "Ignoring %s distances from environment variable, missing colon\n", |
| hwloc_obj_type_string(type)); |
| return; |
| } |
| |
| indexes = calloc(nbobjs, sizeof(unsigned)); |
| distances = calloc(nbobjs*nbobjs, sizeof(float)); |
| tmp = string; |
| |
| /* parse indexes */ |
| for(i=0; i<nbobjs; i++) { |
| indexes[i] = strtoul(tmp, &next, 0); |
| tmp = next+1; |
| } |
| |
| /* parse distances */ |
| z=1; /* default if sscanf finds only 2 values below */ |
| if (sscanf(tmp, "%u*%u*%u", &x, &y, &z) >= 2) { |
| /* generate the matrix to create x groups of y elements */ |
| if (x*y*z != nbobjs) { |
| fprintf(stderr, "Ignoring %s distances from environment variable, invalid grouping (%u*%u*%u=%u instead of %u)\n", |
| hwloc_obj_type_string(type), x, y, z, x*y*z, nbobjs); |
| free(indexes); |
| free(distances); |
| return; |
| } |
| for(i=0; i<nbobjs; i++) |
| for(j=0; j<nbobjs; j++) |
| if (i==j) |
| distances[i*nbobjs+j] = 1; |
| else if (i/z == j/z) |
| distances[i*nbobjs+j] = 2; |
| else if (i/z/y == j/z/y) |
| distances[i*nbobjs+j] = 4; |
| else |
| distances[i*nbobjs+j] = 8; |
| |
| } else { |
| /* parse a comma separated list of distances */ |
| for(i=0; i<nbobjs*nbobjs; i++) { |
| distances[i] = atof(tmp); |
| next = strchr(tmp, ','); |
| if (next) { |
| tmp = next+1; |
| } else if (i!=nbobjs*nbobjs-1) { |
| fprintf(stderr, "Ignoring %s distances from environment variable, not enough values (%u out of %u)\n", |
| hwloc_obj_type_string(type), i+1, nbobjs*nbobjs); |
| free(indexes); |
| free(distances); |
| return; |
| } |
| } |
| } |
| |
| if (hwloc_topology__check_distance_matrix(topology, type, nbobjs, indexes, NULL, distances) < 0) { |
| fprintf(stderr, "Ignoring invalid %s distances from environment variable\n", hwloc_obj_type_string(type)); |
| free(indexes); |
| free(distances); |
| return; |
| } |
| |
| hwloc_topology__set_distance_matrix(topology, type, nbobjs, indexes, NULL, distances); |
| } |
| |
| /* take distances in the environment, store them as is in the topology. |
| * we'll convert them into object later once the tree is filled |
| */ |
| void hwloc_store_distances_from_env(struct hwloc_topology *topology) |
| { |
| hwloc_obj_type_t type; |
| for(type = HWLOC_OBJ_SYSTEM; type < HWLOC_OBJ_TYPE_MAX; type++) { |
| char *env, envname[64]; |
| snprintf(envname, sizeof(envname), "HWLOC_%s_DISTANCES", hwloc_obj_type_string(type)); |
| env = getenv(envname); |
| if (env) |
| hwloc_get_type_distances_from_string(topology, type, env); |
| } |
| } |
| |
| /* take the given distance, store them as is in the topology. |
| * we'll convert them into object later once the tree is filled. |
| */ |
| int hwloc_topology_set_distance_matrix(hwloc_topology_t __hwloc_restrict topology, hwloc_obj_type_t type, |
| unsigned nbobjs, unsigned *indexes, float *distances) |
| { |
| unsigned *_indexes; |
| float *_distances; |
| |
| if (hwloc_topology__check_distance_matrix(topology, type, nbobjs, indexes, NULL, distances) < 0) |
| return -1; |
| |
| /* copy the input arrays and give them to the topology */ |
| _indexes = malloc(nbobjs*sizeof(unsigned)); |
| memcpy(_indexes, indexes, nbobjs*sizeof(unsigned)); |
| _distances = malloc(nbobjs*nbobjs*sizeof(float)); |
| memcpy(_distances, distances, nbobjs*nbobjs*sizeof(float)); |
| hwloc_topology__set_distance_matrix(topology, type, nbobjs, _indexes, NULL, _distances); |
| |
| return 0; |
| } |
| |
| /* cleanup everything we created from distances so that we may rebuild them |
| * at the end of restrict() |
| */ |
| void hwloc_restrict_distances(struct hwloc_topology *topology, unsigned long flags) |
| { |
| hwloc_obj_type_t type; |
| for(type = HWLOC_OBJ_SYSTEM; type < HWLOC_OBJ_TYPE_MAX; type++) { |
| /* remove the objs array, we'll rebuild it from the indexes |
| * depending on remaining objects */ |
| free(topology->os_distances[type].objs); |
| topology->os_distances[type].objs = NULL; |
| /* if not adapting distances, drop everything */ |
| if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_DISTANCES)) { |
| free(topology->os_distances[type].indexes); |
| topology->os_distances[type].indexes = NULL; |
| free(topology->os_distances[type].distances); |
| topology->os_distances[type].distances = NULL; |
| topology->os_distances[type].nbobjs = 0; |
| } |
| } |
| } |
| |
| /* convert distance indexes that were previously stored in the topology |
| * into actual objects if not done already. |
| * it's already done when distances come from backends. |
| * it's not done when distances come from the user. |
| */ |
| void hwloc_convert_distances_indexes_into_objects(struct hwloc_topology *topology) |
| { |
| hwloc_obj_type_t type; |
| for(type = HWLOC_OBJ_SYSTEM; type < HWLOC_OBJ_TYPE_MAX; type++) { |
| unsigned nbobjs = topology->os_distances[type].nbobjs; |
| unsigned *indexes = topology->os_distances[type].indexes; |
| float *distances = topology->os_distances[type].distances; |
| unsigned i, j; |
| if (!topology->os_distances[type].objs) { |
| hwloc_obj_t *objs = calloc(nbobjs, sizeof(hwloc_obj_t)); |
| /* traverse the topology and look for the relevant objects */ |
| for(i=0; i<nbobjs; i++) { |
| hwloc_obj_t obj = hwloc_find_obj_by_type_and_os_index(topology->levels[0][0], type, indexes[i]); |
| if (!obj) { |
| |
| /* shift the matrix */ |
| #define OLDPOS(i,j) (distances+(i)*nbobjs+(j)) |
| #define NEWPOS(i,j) (distances+(i)*(nbobjs-1)+(j)) |
| if (i>0) { |
| /** no need to move beginning of 0th line */ |
| for(j=0; j<i-1; j++) |
| /** move end of jth line + beginning of (j+1)th line */ |
| memmove(NEWPOS(j,i), OLDPOS(j,i+1), (nbobjs-1)*sizeof(*distances)); |
| /** move end of (i-1)th line */ |
| memmove(NEWPOS(i-1,i), OLDPOS(i-1,i+1), (nbobjs-i-1)*sizeof(*distances)); |
| } |
| if (i<nbobjs-1) { |
| /** move beginning of (i+1)th line */ |
| memmove(NEWPOS(i,0), OLDPOS(i+1,0), i*sizeof(*distances)); |
| /** move end of jth line + beginning of (j+1)th line */ |
| for(j=i; j<nbobjs-1; j++) |
| memmove(NEWPOS(j,i), OLDPOS(j+1,i+1), (nbobjs-1)*sizeof(*distances)); |
| /** move end of (nbobjs-2)th line */ |
| memmove(NEWPOS(nbobjs-2,i), OLDPOS(nbobjs-1,i+1), (nbobjs-i-1)*sizeof(*distances)); |
| } |
| |
| /* shift the indexes array */ |
| memmove(indexes+i, indexes+i+1, (nbobjs-i-1)*sizeof(*indexes)); |
| |
| /* update counters */ |
| nbobjs--; |
| i--; |
| continue; |
| } |
| objs[i] = obj; |
| } |
| |
| topology->os_distances[type].nbobjs = nbobjs; |
| if (!nbobjs) { |
| /* the whole matrix was invalid */ |
| free(objs); |
| free(topology->os_distances[type].indexes); |
| topology->os_distances[type].indexes = NULL; |
| free(topology->os_distances[type].distances); |
| topology->os_distances[type].distances = NULL; |
| } else { |
| /* setup the objs array */ |
| topology->os_distances[type].objs = objs; |
| } |
| } |
| } |
| } |
| |
| static void |
| hwloc_setup_distances_from_os_matrix(struct hwloc_topology *topology, |
| unsigned nbobjs, |
| hwloc_obj_t *objs, float *osmatrix) |
| { |
| unsigned i, j, li, lj, minl; |
| float min = FLT_MAX, max = FLT_MIN; |
| hwloc_obj_t root; |
| float *matrix; |
| hwloc_cpuset_t set; |
| unsigned relative_depth; |
| int idx; |
| |
| /* find the root */ |
| set = hwloc_bitmap_alloc(); |
| for(i=0; i<nbobjs; i++) |
| hwloc_bitmap_or(set, set, objs[i]->cpuset); |
| root = hwloc_get_obj_covering_cpuset(topology, set); |
| assert(root); |
| if (!hwloc_bitmap_isequal(set, root->cpuset)) { |
| /* partial distance matrix not including all the children of a single object */ |
| /* TODO insert an intermediate object (group?) covering only these children ? */ |
| hwloc_bitmap_free(set); |
| return; |
| } |
| hwloc_bitmap_free(set); |
| relative_depth = objs[0]->depth - root->depth; /* this assume that we have distances between objects of the same level */ |
| |
| /* get the logical index offset, it's the min of all logical indexes */ |
| minl = UINT_MAX; |
| for(i=0; i<nbobjs; i++) |
| if (minl > objs[i]->logical_index) |
| minl = objs[i]->logical_index; |
| |
| /* compute/check min/max values */ |
| for(i=0; i<nbobjs; i++) |
| for(j=0; j<nbobjs; j++) { |
| float val = osmatrix[i*nbobjs+j]; |
| if (val < min) |
| min = val; |
| if (val > max) |
| max = val; |
| } |
| if (!min) { |
| /* Linux up to 2.6.36 reports ACPI SLIT distances, which should be memory latencies. |
| * Except of SGI IP27 (SGI Origin 200/2000 with MIPS processors) where the distances |
| * are the number of hops between routers. |
| */ |
| hwloc_debug("%s", "minimal distance is 0, matrix does not seem to contain latencies, ignoring\n"); |
| return; |
| } |
| |
| /* store the normalized latency matrix in the root object */ |
| idx = root->distances_count++; |
| root->distances = realloc(root->distances, root->distances_count * sizeof(struct hwloc_distances_s *)); |
| root->distances[idx] = malloc(sizeof(struct hwloc_distances_s)); |
| root->distances[idx]->relative_depth = relative_depth; |
| root->distances[idx]->nbobjs = nbobjs; |
| root->distances[idx]->latency = matrix = malloc(nbobjs*nbobjs*sizeof(float)); |
| root->distances[idx]->latency_base = (float) min; |
| #define NORMALIZE_LATENCY(d) ((d)/(min)) |
| root->distances[idx]->latency_max = NORMALIZE_LATENCY(max); |
| for(i=0; i<nbobjs; i++) { |
| li = objs[i]->logical_index - minl; |
| matrix[li*nbobjs+li] = NORMALIZE_LATENCY(osmatrix[i*nbobjs+i]); |
| for(j=i+1; j<nbobjs; j++) { |
| lj = objs[j]->logical_index - minl; |
| matrix[li*nbobjs+lj] = NORMALIZE_LATENCY(osmatrix[i*nbobjs+j]); |
| matrix[lj*nbobjs+li] = NORMALIZE_LATENCY(osmatrix[j*nbobjs+i]); |
| } |
| } |
| } |
| |
| /* convert internal distances into logically-ordered distances |
| * that can be exposed in the API |
| */ |
| void |
| hwloc_finalize_logical_distances(struct hwloc_topology *topology) |
| { |
| unsigned nbobjs; |
| hwloc_obj_type_t type; |
| int depth; |
| |
| for (type = HWLOC_OBJ_SYSTEM; type < HWLOC_OBJ_TYPE_MAX; type++) { |
| nbobjs = topology->os_distances[type].nbobjs; |
| if (!nbobjs) |
| continue; |
| |
| depth = hwloc_get_type_depth(topology, type); |
| if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) |
| continue; |
| |
| if (topology->os_distances[type].objs) { |
| assert(topology->os_distances[type].distances); |
| |
| hwloc_setup_distances_from_os_matrix(topology, nbobjs, |
| topology->os_distances[type].objs, |
| topology->os_distances[type].distances); |
| } |
| } |
| } |
| |
| /* destroy a object distances structure */ |
| void |
| hwloc_free_logical_distances(struct hwloc_distances_s * dist) |
| { |
| free(dist->latency); |
| free(dist); |
| } |
| |
| static void hwloc_report_user_distance_error(const char *msg, int line) |
| { |
| static int reported = 0; |
| |
| if (!reported) { |
| fprintf(stderr, "****************************************************************************\n"); |
| fprintf(stderr, "* Hwloc has encountered what looks like an error from user-given distances.\n"); |
| fprintf(stderr, "*\n"); |
| fprintf(stderr, "* %s\n", msg); |
| fprintf(stderr, "* Error occurred in topology.c line %d\n", line); |
| fprintf(stderr, "*\n"); |
| fprintf(stderr, "* Please make sure that distances given through the interface or environment\n"); |
| fprintf(stderr, "* variables do not contradict any other topology information.\n"); |
| fprintf(stderr, "****************************************************************************\n"); |
| reported = 1; |
| } |
| } |
| |
| /* |
| * Place objects in groups if they are in a transitive graph of minimal distances. |
| * Return how many groups were created, or 0 if some incomplete distance graphs were found. |
| */ |
| static unsigned |
| hwloc_setup_group_from_min_distance(unsigned nbobjs, |
| float *_distances, |
| unsigned *groupids) |
| { |
| float min_distance = FLT_MAX; |
| unsigned groupid = 1; |
| unsigned i,j,k; |
| unsigned skipped = 0; |
| |
| #define DISTANCE(i, j) _distances[(i) * nbobjs + (j)] |
| |
| memset(groupids, 0, nbobjs*sizeof(*groupids)); |
| |
| /* find the minimal distance */ |
| for(i=0; i<nbobjs; i++) |
| for(j=i+1; j<nbobjs; j++) |
| if (DISTANCE(i, j) < min_distance) |
| min_distance = DISTANCE(i, j); |
| hwloc_debug("found minimal distance %f between objects\n", min_distance); |
| |
| if (min_distance == FLT_MAX) |
| return 0; |
| |
| /* build groups of objects connected with this distance */ |
| for(i=0; i<nbobjs; i++) { |
| unsigned size; |
| int firstfound; |
| |
| /* if already grouped, skip */ |
| if (groupids[i]) |
| continue; |
| |
| /* start a new group */ |
| groupids[i] = groupid; |
| size = 1; |
| firstfound = i; |
| |
| while (firstfound != -1) { |
| /* we added new objects to the group, the first one was firstfound. |
| * rescan all connections from these new objects (starting at first found) to any other objects, |
| * so as to find new objects minimally-connected by transivity. |
| */ |
| int newfirstfound = -1; |
| for(j=firstfound; j<nbobjs; j++) |
| if (groupids[j] == groupid) |
| for(k=0; k<nbobjs; k++) |
| if (!groupids[k] && DISTANCE(j, k) == min_distance) { |
| groupids[k] = groupid; |
| size++; |
| if (newfirstfound == -1) |
| newfirstfound = k; |
| if (i == j) |
| hwloc_debug("object %u is minimally connected to %u\n", k, i); |
| else |
| hwloc_debug("object %u is minimally connected to %u through %u\n", k, i, j); |
| } |
| firstfound = newfirstfound; |
| } |
| |
| if (size == 1) { |
| /* cancel this useless group, ignore this object and try from the next one */ |
| groupids[i] = 0; |
| skipped++; |
| continue; |
| } |
| |
| /* valid this group */ |
| groupid++; |
| hwloc_debug("found transitive graph with %u objects with minimal distance %f\n", |
| size, min_distance); |
| } |
| |
| if (groupid == 2 && !skipped) |
| /* we created a single group containing all objects, ignore it */ |
| return 0; |
| |
| /* return the last id, since it's also the number of used group ids */ |
| return groupid-1; |
| } |
| |
| /* |
| * Look at object physical distances to group them, |
| * after having done some basic sanity checks. |
| */ |
| static void |
| hwloc__setup_groups_from_distances(struct hwloc_topology *topology, |
| unsigned nbobjs, |
| struct hwloc_obj **objs, |
| float *_distances, |
| int fromuser) |
| { |
| unsigned *groupids = NULL; |
| unsigned nbgroups; |
| unsigned i,j; |
| |
| hwloc_debug("trying to group %s objects into Group objects according to physical distances\n", |
| hwloc_obj_type_string(objs[0]->type)); |
| |
| if (nbobjs <= 2) { |
| return; |
| } |
| |
| groupids = malloc(sizeof(unsigned) * nbobjs); |
| if (NULL == groupids) { |
| return; |
| } |
| |
| nbgroups = hwloc_setup_group_from_min_distance(nbobjs, _distances, groupids); |
| if (!nbgroups) { |
| goto outter_free; |
| } |
| |
| /* For convenience, put these declarations inside a block. It's a |
| crying shame we can't use C99 syntax here, and have to do a bunch |
| of mallocs. :-( */ |
| { |
| hwloc_obj_t *groupobjs = NULL; |
| unsigned *groupsizes = NULL; |
| float *groupdistances = NULL; |
| |
| groupobjs = malloc(sizeof(hwloc_obj_t) * nbgroups); |
| groupsizes = malloc(sizeof(unsigned) * nbgroups); |
| groupdistances = malloc(sizeof(float) * nbgroups * nbgroups); |
| if (NULL == groupobjs || NULL == groupsizes || NULL == groupdistances) { |
| goto inner_free; |
| } |
| /* create new Group objects and record their size */ |
| memset(&(groupsizes[0]), 0, sizeof(groupsizes[0]) * nbgroups); |
| for(i=0; i<nbgroups; i++) { |
| /* create the Group object */ |
| hwloc_obj_t group_obj; |
| group_obj = hwloc_alloc_setup_object(HWLOC_OBJ_GROUP, -1); |
| group_obj->cpuset = hwloc_bitmap_alloc(); |
| group_obj->attr->group.depth = topology->next_group_depth; |
| for (j=0; j<nbobjs; j++) |
| if (groupids[j] == i+1) { |
| hwloc_bitmap_or(group_obj->cpuset, group_obj->cpuset, objs[j]->cpuset); |
| groupsizes[i]++; |
| } |
| hwloc_debug_1arg_bitmap("adding Group object with %u objects and cpuset %s\n", |
| groupsizes[i], group_obj->cpuset); |
| hwloc__insert_object_by_cpuset(topology, group_obj, |
| fromuser ? hwloc_report_user_distance_error : hwloc_report_os_error); |
| groupobjs[i] = group_obj; |
| } |
| |
| /* factorize distances */ |
| memset(&(groupdistances[0]), 0, sizeof(groupdistances[0]) * nbgroups * nbgroups); |
| #undef DISTANCE |
| #define DISTANCE(i, j) _distances[(i) * nbobjs + (j)] |
| #define GROUP_DISTANCE(i, j) groupdistances[(i) * nbgroups + (j)] |
| for(i=0; i<nbobjs; i++) |
| if (groupids[i]) |
| for(j=0; j<nbobjs; j++) |
| if (groupids[j]) |
| GROUP_DISTANCE(groupids[i]-1, groupids[j]-1) += DISTANCE(i, j); |
| for(i=0; i<nbgroups; i++) |
| for(j=0; j<nbgroups; j++) |
| GROUP_DISTANCE(i, j) /= groupsizes[i]*groupsizes[j]; |
| #ifdef HWLOC_DEBUG |
| hwloc_debug("%s", "generated new distance matrix between groups:\n"); |
| hwloc_debug("%s", " index"); |
| for(j=0; j<nbgroups; j++) |
| hwloc_debug(" % 5d", (int) j); /* print index because os_index is -1 fro Groups */ |
| hwloc_debug("%s", "\n"); |
| for(i=0; i<nbgroups; i++) { |
| hwloc_debug(" % 5d", (int) i); |
| for(j=0; j<nbgroups; j++) |
| hwloc_debug(" %2.3f", GROUP_DISTANCE(i, j)); |
| hwloc_debug("%s", "\n"); |
| } |
| #endif |
| |
| topology->next_group_depth++; |
| hwloc__setup_groups_from_distances(topology, nbgroups, groupobjs, (float*) groupdistances, fromuser); |
| |
| inner_free: |
| /* Safely free everything */ |
| if (NULL != groupobjs) { |
| free(groupobjs); |
| } |
| if (NULL != groupsizes) { |
| free(groupsizes); |
| } |
| if (NULL != groupdistances) { |
| free(groupdistances); |
| } |
| } |
| |
| outter_free: |
| if (NULL != groupids) { |
| free(groupids); |
| } |
| } |
| |
| /* |
| * Look at object physical distances to group them. |
| */ |
| static void |
| hwloc_setup_groups_from_distances(struct hwloc_topology *topology, |
| unsigned nbobjs, |
| struct hwloc_obj **objs, |
| float *_distances, |
| int fromuser) |
| { |
| unsigned i,j; |
| |
| if (getenv("HWLOC_IGNORE_DISTANCES")) |
| return; |
| |
| #ifdef HWLOC_DEBUG |
| hwloc_debug("%s", "trying to group objects using distance matrix:\n"); |
| hwloc_debug("%s", " index"); |
| for(j=0; j<nbobjs; j++) |
| hwloc_debug(" % 5d", (int) objs[j]->os_index); |
| hwloc_debug("%s", "\n"); |
| for(i=0; i<nbobjs; i++) { |
| hwloc_debug(" % 5d", (int) objs[i]->os_index); |
| for(j=0; j<nbobjs; j++) |
| hwloc_debug(" %2.3f", DISTANCE(i, j)); |
| hwloc_debug("%s", "\n"); |
| } |
| #endif |
| |
| /* check that the matrix is ok */ |
| for(i=0; i<nbobjs; i++) { |
| for(j=i+1; j<nbobjs; j++) { |
| /* should be symmetric */ |
| if (DISTANCE(i, j) != DISTANCE(j, i)) { |
| hwloc_debug("distance matrix asymmetric ([%u,%u]=%f != [%u,%u]=%f), aborting\n", |
| i, j, DISTANCE(i, j), j, i, DISTANCE(j, i)); |
| return; |
| } |
| /* diagonal is smaller than everything else */ |
| if (DISTANCE(i, j) <= DISTANCE(i, i)) { |
| hwloc_debug("distance to self not strictly minimal ([%u,%u]=%f <= [%u,%u]=%f), aborting\n", |
| i, j, DISTANCE(i, j), i, i, DISTANCE(i, i)); |
| return; |
| } |
| } |
| } |
| |
| hwloc__setup_groups_from_distances(topology, nbobjs, objs, _distances, fromuser); |
| } |
| |
| void |
| hwloc_group_by_distances(struct hwloc_topology *topology) |
| { |
| unsigned nbobjs; |
| hwloc_obj_type_t type; |
| |
| for (type = HWLOC_OBJ_SYSTEM; type < HWLOC_OBJ_TYPE_MAX; type++) { |
| nbobjs = topology->os_distances[type].nbobjs; |
| if (!nbobjs) |
| continue; |
| |
| if (topology->os_distances[type].objs) { |
| /* if we have objs, we must have distances as well, |
| * thanks to hwloc_convert_distances_indexes_into_objects() |
| */ |
| assert(topology->os_distances[type].distances); |
| hwloc_setup_groups_from_distances(topology, nbobjs, |
| topology->os_distances[type].objs, |
| topology->os_distances[type].distances, |
| topology->os_distances[type].indexes != NULL); |
| } |
| } |
| } |