#ifndef LIBOMP_TEST_TOPOLOGY_H #define LIBOMP_TEST_TOPOLOGY_H #include "libomp_test_affinity.h" #include #include #include #include #include #include #include typedef enum topology_obj_type_t { TOPOLOGY_OBJ_THREAD, TOPOLOGY_OBJ_CORE, TOPOLOGY_OBJ_SOCKET, TOPOLOGY_OBJ_MAX } topology_obj_type_t; typedef struct place_list_t { int num_places; int current_place; int *place_nums; affinity_mask_t **masks; } place_list_t; // Return the first character in file 'f' that is not a whitespace character // including newlines and carriage returns static int get_first_nonspace_from_file(FILE *f) { int c; do { c = fgetc(f); } while (c != EOF && (isspace(c) || c == '\n' || c == '\r')); return c; } // Read an integer from file 'f' into 'number' // Return 1 on successful read of integer, // 0 on unsuccessful read of integer, // EOF on end of file. static int get_integer_from_file(FILE *f, int *number) { int n; n = fscanf(f, "%d", number); if (feof(f)) return EOF; if (n != 1) return 0; return 1; } // Read a siblings list file from Linux /sys/devices/system/cpu/cpu?/topology/* static affinity_mask_t *topology_get_mask_from_file(const char *filename) { int status = EXIT_SUCCESS; FILE *f = fopen(filename, "r"); if (!f) { perror(filename); exit(EXIT_FAILURE); } affinity_mask_t *mask = affinity_mask_alloc(); while (1) { int c, i, n, lower, upper; // Read the first integer n = get_integer_from_file(f, &lower); if (n == EOF) { break; } else if (n == 0) { fprintf(stderr, "syntax error: expected integer\n"); status = EXIT_FAILURE; break; } // Now either a , or - c = get_first_nonspace_from_file(f); if (c == EOF || c == ',') { affinity_mask_set(mask, lower); if (c == EOF) break; } else if (c == '-') { n = get_integer_from_file(f, &upper); if (n == EOF || n == 0) { fprintf(stderr, "syntax error: expected integer\n"); status = EXIT_FAILURE; break; } for (i = lower; i <= upper; ++i) affinity_mask_set(mask, i); c = get_first_nonspace_from_file(f); if (c == EOF) { break; } else if (c == ',') { continue; } else { fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c, c); status = EXIT_FAILURE; break; } } else { fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c, c); status = EXIT_FAILURE; break; } } fclose(f); if (status == EXIT_FAILURE) { affinity_mask_free(mask); mask = NULL; } return mask; } static int topology_get_num_cpus() { char buf[1024]; // Count the number of cpus int cpu = 0; while (1) { snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%d", cpu); DIR *dir = opendir(buf); if (dir) { closedir(dir); cpu++; } else { break; } } if (cpu == 0) cpu = 1; return cpu; } // Return whether the current thread has access to all logical processors static int topology_using_full_mask() { int cpu; int has_all = 1; int num_cpus = topology_get_num_cpus(); affinity_mask_t *mask = affinity_mask_alloc(); get_thread_affinity(mask); for (cpu = 0; cpu < num_cpus; ++cpu) { if (!affinity_mask_isset(mask, cpu)) { has_all = 0; break; } } affinity_mask_free(mask); return has_all; } // Return array of masks representing OMP_PLACES keyword (e.g., sockets, cores, // threads) static place_list_t *topology_alloc_type_places(topology_obj_type_t type) { char buf[1024]; int i, cpu, num_places, num_unique; int *place_nums; int num_cpus = topology_get_num_cpus(); place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t)); affinity_mask_t **masks = (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_cpus); num_unique = 0; for (cpu = 0; cpu < num_cpus; ++cpu) { affinity_mask_t *mask; if (type == TOPOLOGY_OBJ_CORE) { snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu); mask = topology_get_mask_from_file(buf); } else if (type == TOPOLOGY_OBJ_SOCKET) { snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list", cpu); mask = topology_get_mask_from_file(buf); } else if (type == TOPOLOGY_OBJ_THREAD) { mask = affinity_mask_alloc(); affinity_mask_set(mask, cpu); } else { fprintf(stderr, "Unknown topology type (%d)\n", (int)type); exit(EXIT_FAILURE); } // Check for unique topology objects above the thread level if (type != TOPOLOGY_OBJ_THREAD) { for (i = 0; i < num_unique; ++i) { if (affinity_mask_equal(masks[i], mask)) { affinity_mask_free(mask); mask = NULL; break; } } } if (mask) masks[num_unique++] = mask; } place_nums = (int *)malloc(sizeof(int) * num_unique); for (i = 0; i < num_unique; ++i) place_nums[i] = i; places->num_places = num_unique; places->masks = masks; places->place_nums = place_nums; places->current_place = -1; return places; } static place_list_t *topology_alloc_openmp_places() { int place, i; int num_places = omp_get_num_places(); place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t)); affinity_mask_t **masks = (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_places); int *place_nums = (int *)malloc(sizeof(int) * num_places); for (place = 0; place < num_places; ++place) { int num_procs = omp_get_place_num_procs(place); int *ids = (int *)malloc(sizeof(int) * num_procs); omp_get_place_proc_ids(place, ids); affinity_mask_t *mask = affinity_mask_alloc(); for (i = 0; i < num_procs; ++i) affinity_mask_set(mask, ids[i]); masks[place] = mask; place_nums[place] = place; } places->num_places = num_places; places->place_nums = place_nums; places->masks = masks; places->current_place = omp_get_place_num(); return places; } static place_list_t *topology_alloc_openmp_partition() { int p, i; int num_places = omp_get_partition_num_places(); place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t)); int *place_nums = (int *)malloc(sizeof(int) * num_places); affinity_mask_t **masks = (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_places); omp_get_partition_place_nums(place_nums); for (p = 0; p < num_places; ++p) { int place = place_nums[p]; int num_procs = omp_get_place_num_procs(place); int *ids = (int *)malloc(sizeof(int) * num_procs); if (num_procs == 0) { fprintf(stderr, "place %d has 0 procs?\n", place); exit(EXIT_FAILURE); } omp_get_place_proc_ids(place, ids); affinity_mask_t *mask = affinity_mask_alloc(); for (i = 0; i < num_procs; ++i) affinity_mask_set(mask, ids[i]); if (affinity_mask_count(mask) == 0) { fprintf(stderr, "place %d has 0 procs set?\n", place); exit(EXIT_FAILURE); } masks[p] = mask; } places->num_places = num_places; places->place_nums = place_nums; places->masks = masks; places->current_place = omp_get_place_num(); return places; } // Free the array of masks from one of: topology_alloc_type_masks() // or topology_alloc_openmp_masks() static void topology_free_places(place_list_t *places) { int i; for (i = 0; i < places->num_places; ++i) affinity_mask_free(places->masks[i]); free(places->masks); free(places->place_nums); free(places); } static void topology_print_places(const place_list_t *p) { int i; char buf[1024]; for (i = 0; i < p->num_places; ++i) { affinity_mask_snprintf(buf, sizeof(buf), p->masks[i]); printf("Place %d: %s\n", p->place_nums[i], buf); } } // Print out an error message, possibly with two problem place lists, // and then exit with failure static void proc_bind_die(omp_proc_bind_t proc_bind, int T, int P, const char *format, ...) { va_list args; va_start(args, format); const char *pb; switch (proc_bind) { case omp_proc_bind_false: pb = "False"; break; case omp_proc_bind_true: pb = "True"; break; case omp_proc_bind_master: pb = "Master (Primary)"; break; case omp_proc_bind_close: pb = "Close"; break; case omp_proc_bind_spread: pb = "Spread"; break; default: pb = "(Unknown Proc Bind Type)"; break; } if (proc_bind == omp_proc_bind_spread || proc_bind == omp_proc_bind_close) { if (T <= P) { fprintf(stderr, "%s : (T(%d) <= P(%d)) : ", pb, T, P); } else { fprintf(stderr, "%s : (T(%d) > P(%d)) : ", pb, T, P); } } else { fprintf(stderr, "%s : T = %d, P = %d : ", pb, T, P); } vfprintf(stderr, format, args); va_end(args); exit(EXIT_FAILURE); } // Return 1 on failure, 0 on success. static void proc_bind_check(omp_proc_bind_t proc_bind, const place_list_t *parent, place_list_t **children, int nchildren) { place_list_t *partition; int T, i, j, place, low, high, first, last, count, current_place, num_places; const int *place_nums; int P = parent->num_places; // Find the correct T (there could be null entries in children) place_list_t **partitions = (place_list_t **)malloc(sizeof(place_list_t *) * nchildren); T = 0; for (i = 0; i < nchildren; ++i) if (children[i]) partitions[T++] = children[i]; // Only able to check spread, close, master (primary) if (proc_bind != omp_proc_bind_spread && proc_bind != omp_proc_bind_close && proc_bind != omp_proc_bind_master) proc_bind_die(proc_bind, T, P, NULL, NULL, "Cannot check this proc bind type\n"); if (proc_bind == omp_proc_bind_spread) { if (T <= P) { // Run through each subpartition for (i = 0; i < T; ++i) { partition = partitions[i]; place_nums = partition->place_nums; num_places = partition->num_places; current_place = partition->current_place; // Correct count? low = P / T; high = P / T + (P % T ? 1 : 0); if (num_places != low && num_places != high) { proc_bind_die(proc_bind, T, P, "Incorrect number of places for thread %d: %d. " "Expecting between %d and %d\n", i, num_places, low, high); } // Consecutive places? for (j = 1; j < num_places; ++j) { if (place_nums[j] != (place_nums[j - 1] + 1) % P) { proc_bind_die(proc_bind, T, P, "Not consecutive places: %d, %d in partition\n", place_nums[j - 1], place_nums[j]); } } first = place_nums[0]; last = place_nums[num_places - 1]; // Primary thread executes on place of the parent thread? if (i == 0) { if (current_place != parent->current_place) { proc_bind_die( proc_bind, T, P, "Primary thread not on same place (%d) as parent thread (%d)\n", current_place, parent->current_place); } } else { // Thread's current place is first place within it's partition? if (current_place != first) { proc_bind_die(proc_bind, T, P, "Thread's current place (%d) is not the first place " "in its partition [%d, %d]\n", current_place, first, last); } } // Partitions don't have intersections? int f1 = first; int l1 = last; for (j = 0; j < i; ++j) { int f2 = partitions[j]->place_nums[0]; int l2 = partitions[j]->place_nums[partitions[j]->num_places - 1]; if (f1 > l1 && f2 > l2) { proc_bind_die(proc_bind, T, P, "partitions intersect. [%d, %d] and [%d, %d]\n", f1, l1, f2, l2); } if (f1 > l1 && f2 <= l2) if (f1 < l2 || l1 > f2) { proc_bind_die(proc_bind, T, P, "partitions intersect. [%d, %d] and [%d, %d]\n", f1, l1, f2, l2); } if (f1 <= l1 && f2 > l2) if (f2 < l1 || l2 > f1) { proc_bind_die(proc_bind, T, P, "partitions intersect. [%d, %d] and [%d, %d]\n", f1, l1, f2, l2); } if (f1 <= l1 && f2 <= l2) if (!(f2 > l1 || l2 < f1)) { proc_bind_die(proc_bind, T, P, "partitions intersect. [%d, %d] and [%d, %d]\n", f1, l1, f2, l2); } } } } else { // T > P // Each partition has only one place? for (i = 0; i < T; ++i) { if (partitions[i]->num_places != 1) { proc_bind_die( proc_bind, T, P, "Incorrect number of places for thread %d: %d. Expecting 1\n", i, partitions[i]->num_places); } } // Correct number of consecutive threads per partition? low = T / P; high = T / P + (T % P ? 1 : 0); for (i = 1, count = 1; i < T; ++i) { if (partitions[i]->place_nums[0] == partitions[i - 1]->place_nums[0]) { count++; if (count > high) { proc_bind_die( proc_bind, T, P, "Too many threads have place %d for their partition\n", partitions[i]->place_nums[0]); } } else { if (count < low) { proc_bind_die( proc_bind, T, P, "Not enough threads have place %d for their partition\n", partitions[i]->place_nums[0]); } count = 1; } } // Primary thread executes on place of the parent thread? current_place = partitions[0]->place_nums[0]; if (parent->current_place != -1 && current_place != parent->current_place) { proc_bind_die( proc_bind, T, P, "Primary thread not on same place (%d) as parent thread (%d)\n", current_place, parent->current_place); } } } else if (proc_bind == omp_proc_bind_close || proc_bind == omp_proc_bind_master) { // Check that each subpartition is the same as the parent for (i = 0; i < T; ++i) { partition = partitions[i]; place_nums = partition->place_nums; num_places = partition->num_places; current_place = partition->current_place; if (parent->num_places != num_places) { proc_bind_die(proc_bind, T, P, "Number of places in subpartition (%d) does not match " "parent (%d)\n", num_places, parent->num_places); } for (j = 0; j < num_places; ++j) { if (parent->place_nums[j] != place_nums[j]) { proc_bind_die(proc_bind, T, P, "Subpartition place (%d) does not match " "parent partition place (%d)\n", place_nums[j], parent->place_nums[j]); } } } // Find index into place_nums of current place for parent for (j = 0; j < parent->num_places; ++j) if (parent->place_nums[j] == parent->current_place) break; if (proc_bind == omp_proc_bind_close) { if (T <= P) { // close T <= P // check place assignment for each thread for (i = 0; i < T; ++i) { partition = partitions[i]; current_place = partition->current_place; if (current_place != parent->place_nums[j]) { proc_bind_die( proc_bind, T, P, "Thread %d's current place (%d) is incorrect. expected %d\n", i, current_place, parent->place_nums[j]); } j = (j + 1) % parent->num_places; } } else { // close T > P // check place assignment for each thread low = T / P; high = T / P + (T % P ? 1 : 0); count = 1; if (partitions[0]->current_place != parent->current_place) { proc_bind_die( proc_bind, T, P, "Primary thread's place (%d) is not parent thread's place (%d)\n", partitions[0]->current_place, parent->current_place); } for (i = 1; i < T; ++i) { current_place = partitions[i]->current_place; if (current_place == parent->place_nums[j]) { count++; if (count > high) { proc_bind_die( proc_bind, T, P, "Too many threads have place %d for their current place\n", current_place); } } else { if (count < low) { proc_bind_die( proc_bind, T, P, "Not enough threads have place %d for their current place\n", parent->place_nums[j]); } j = (j + 1) % parent->num_places; if (current_place != parent->place_nums[j]) { proc_bind_die( proc_bind, T, P, "Thread %d's place (%d) is not corret. Expected %d\n", i, partitions[i]->current_place, parent->place_nums[j]); } count = 1; } } } } else { // proc_bind_primary // Every thread should be assigned to the primary thread's place for (i = 0; i < T; ++i) { if (partitions[i]->current_place != parent->current_place) { proc_bind_die( proc_bind, T, P, "Thread %d's place (%d) is not the primary thread's place (%d)\n", i, partitions[i]->current_place, parent->current_place); } } } } // Check that each partition's current place is within the partition for (i = 0; i < T; ++i) { current_place = partitions[i]->current_place; num_places = partitions[i]->num_places; first = partitions[i]->place_nums[0]; last = partitions[i]->place_nums[num_places - 1]; for (j = 0; j < num_places; ++j) if (partitions[i]->place_nums[j] == current_place) break; if (j == num_places) { proc_bind_die(proc_bind, T, P, "Thread %d's current place (%d) is not within its " "partition [%d, %d]\n", i, current_place, first, last); } } free(partitions); } #endif