/* * 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 #include #include #include #include /* 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; itype == 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= 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; ios_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; ilevels[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; jos_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; icpuset); 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 objs[i]->logical_index) minl = objs[i]->logical_index; /* compute/check min/max values */ for(i=0; i 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; ilogical_index - minl; matrix[li*nbobjs+li] = NORMALIZE_LATENCY(osmatrix[i*nbobjs+i]); for(j=i+1; jlogical_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; itype)); 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; icpuset = hwloc_bitmap_alloc(); group_obj->attr->group.depth = topology->next_group_depth; for (j=0; jcpuset, 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; inext_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; jos_index); hwloc_debug("%s", "\n"); for(i=0; ios_index); for(j=0; jos_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); } } }