brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.4 KiB · 410103d Raw
577 lines · c
1#ifndef LIBOMP_TEST_TOPOLOGY_H2#define LIBOMP_TEST_TOPOLOGY_H3 4#include "libomp_test_affinity.h"5#include <stdio.h>6#include <stdlib.h>7#include <dirent.h>8#include <errno.h>9#include <ctype.h>10#include <omp.h>11#include <stdarg.h>12 13typedef enum topology_obj_type_t {14  TOPOLOGY_OBJ_THREAD,15  TOPOLOGY_OBJ_CORE,16  TOPOLOGY_OBJ_SOCKET,17  TOPOLOGY_OBJ_MAX18} topology_obj_type_t;19 20typedef struct place_list_t {21  int num_places;22  int current_place;23  int *place_nums;24  affinity_mask_t **masks;25} place_list_t;26 27// Return the first character in file 'f' that is not a whitespace character28// including newlines and carriage returns29static int get_first_nonspace_from_file(FILE *f) {30  int c;31  do {32    c = fgetc(f);33  } while (c != EOF && (isspace(c) || c == '\n' || c == '\r'));34  return c;35}36 37// Read an integer from file 'f' into 'number'38// Return 1 on successful read of integer,39//        0 on unsuccessful read of integer,40//        EOF on end of file.41static int get_integer_from_file(FILE *f, int *number) {42  int n;43  n = fscanf(f, "%d", number);44  if (feof(f))45    return EOF;46  if (n != 1)47    return 0;48  return 1;49}50 51// Read a siblings list file from Linux /sys/devices/system/cpu/cpu?/topology/*52static affinity_mask_t *topology_get_mask_from_file(const char *filename) {53  int status = EXIT_SUCCESS;54  FILE *f = fopen(filename, "r");55  if (!f) {56    perror(filename);57    exit(EXIT_FAILURE);58  }59  affinity_mask_t *mask = affinity_mask_alloc();60  while (1) {61    int c, i, n, lower, upper;62    // Read the first integer63    n = get_integer_from_file(f, &lower);64    if (n == EOF) {65      break;66    } else if (n == 0) {67      fprintf(stderr, "syntax error: expected integer\n");68      status = EXIT_FAILURE;69      break;70    }71 72    // Now either a , or -73    c = get_first_nonspace_from_file(f);74    if (c == EOF || c == ',') {75      affinity_mask_set(mask, lower);76      if (c == EOF)77        break;78    } else if (c == '-') {79      n = get_integer_from_file(f, &upper);80      if (n == EOF || n == 0) {81        fprintf(stderr, "syntax error: expected integer\n");82        status = EXIT_FAILURE;83        break;84      }85      for (i = lower; i <= upper; ++i)86        affinity_mask_set(mask, i);87      c = get_first_nonspace_from_file(f);88      if (c == EOF) {89        break;90      } else if (c == ',') {91        continue;92      } else {93        fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c,94                c);95        status = EXIT_FAILURE;96        break;97      }98    } else {99      fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c, c);100      status = EXIT_FAILURE;101      break;102    }103  }104  fclose(f);105  if (status == EXIT_FAILURE) {106    affinity_mask_free(mask);107    mask = NULL;108  }109  return mask;110}111 112static int topology_get_num_cpus() {113  char buf[1024];114  // Count the number of cpus115  int cpu = 0;116  while (1) {117    snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%d", cpu);118    DIR *dir = opendir(buf);119    if (dir) {120      closedir(dir);121      cpu++;122    } else {123      break;124    }125  }126  if (cpu == 0)127    cpu = 1;128  return cpu;129}130 131// Return whether the current thread has access to all logical processors132static int topology_using_full_mask() {133  int cpu;134  int has_all = 1;135  int num_cpus = topology_get_num_cpus();136  affinity_mask_t *mask = affinity_mask_alloc();137  get_thread_affinity(mask);138  for (cpu = 0; cpu < num_cpus; ++cpu) {139    if (!affinity_mask_isset(mask, cpu)) {140      has_all = 0;141      break;142    }143  }144  affinity_mask_free(mask);145  return has_all;146}147 148// Return array of masks representing OMP_PLACES keyword (e.g., sockets, cores,149// threads)150static place_list_t *topology_alloc_type_places(topology_obj_type_t type) {151  char buf[1024];152  int i, cpu, num_places, num_unique;153  int *place_nums;154  int num_cpus = topology_get_num_cpus();155  place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));156  affinity_mask_t **masks =157      (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_cpus);158  num_unique = 0;159  for (cpu = 0; cpu < num_cpus; ++cpu) {160    affinity_mask_t *mask;161    if (type == TOPOLOGY_OBJ_CORE) {162      snprintf(buf, sizeof(buf),163               "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",164               cpu);165      mask = topology_get_mask_from_file(buf);166    } else if (type == TOPOLOGY_OBJ_SOCKET) {167      snprintf(buf, sizeof(buf),168               "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",169               cpu);170      mask = topology_get_mask_from_file(buf);171    } else if (type == TOPOLOGY_OBJ_THREAD) {172      mask = affinity_mask_alloc();173      affinity_mask_set(mask, cpu);174    } else {175      fprintf(stderr, "Unknown topology type (%d)\n", (int)type);176      exit(EXIT_FAILURE);177    }178    // Check for unique topology objects above the thread level179    if (type != TOPOLOGY_OBJ_THREAD) {180      for (i = 0; i < num_unique; ++i) {181        if (affinity_mask_equal(masks[i], mask)) {182          affinity_mask_free(mask);183          mask = NULL;184          break;185        }186      }187    }188    if (mask)189      masks[num_unique++] = mask;190  }191  place_nums = (int *)malloc(sizeof(int) * num_unique);192  for (i = 0; i < num_unique; ++i)193    place_nums[i] = i;194  places->num_places = num_unique;195  places->masks = masks;196  places->place_nums = place_nums;197  places->current_place = -1;198  return places;199}200 201static place_list_t *topology_alloc_openmp_places() {202  int place, i;203  int num_places = omp_get_num_places();204  place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));205  affinity_mask_t **masks =206      (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_places);207  int *place_nums = (int *)malloc(sizeof(int) * num_places);208  for (place = 0; place < num_places; ++place) {209    int num_procs = omp_get_place_num_procs(place);210    int *ids = (int *)malloc(sizeof(int) * num_procs);211    omp_get_place_proc_ids(place, ids);212    affinity_mask_t *mask = affinity_mask_alloc();213    for (i = 0; i < num_procs; ++i)214      affinity_mask_set(mask, ids[i]);215    masks[place] = mask;216    place_nums[place] = place;217  }218  places->num_places = num_places;219  places->place_nums = place_nums;220  places->masks = masks;221  places->current_place = omp_get_place_num();222  return places;223}224 225static place_list_t *topology_alloc_openmp_partition() {226  int p, i;227  int num_places = omp_get_partition_num_places();228  place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));229  int *place_nums = (int *)malloc(sizeof(int) * num_places);230  affinity_mask_t **masks =231      (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_places);232  omp_get_partition_place_nums(place_nums);233  for (p = 0; p < num_places; ++p) {234    int place = place_nums[p];235    int num_procs = omp_get_place_num_procs(place);236    int *ids = (int *)malloc(sizeof(int) * num_procs);237    if (num_procs == 0) {238      fprintf(stderr, "place %d has 0 procs?\n", place);239      exit(EXIT_FAILURE);240    }241    omp_get_place_proc_ids(place, ids);242    affinity_mask_t *mask = affinity_mask_alloc();243    for (i = 0; i < num_procs; ++i)244      affinity_mask_set(mask, ids[i]);245    if (affinity_mask_count(mask) == 0) {246      fprintf(stderr, "place %d has 0 procs set?\n", place);247      exit(EXIT_FAILURE);248    }249    masks[p] = mask;250  }251  places->num_places = num_places;252  places->place_nums = place_nums;253  places->masks = masks;254  places->current_place = omp_get_place_num();255  return places;256}257 258// Free the array of masks from one of: topology_alloc_type_masks()259// or topology_alloc_openmp_masks()260static void topology_free_places(place_list_t *places) {261  int i;262  for (i = 0; i < places->num_places; ++i)263    affinity_mask_free(places->masks[i]);264  free(places->masks);265  free(places->place_nums);266  free(places);267}268 269static void topology_print_places(const place_list_t *p) {270  int i;271  char buf[1024];272  for (i = 0; i < p->num_places; ++i) {273    affinity_mask_snprintf(buf, sizeof(buf), p->masks[i]);274    printf("Place %d: %s\n", p->place_nums[i], buf);275  }276}277 278// Print out an error message, possibly with two problem place lists,279// and then exit with failure280static void proc_bind_die(omp_proc_bind_t proc_bind, int T, int P,281                          const char *format, ...) {282  va_list args;283  va_start(args, format);284  const char *pb;285  switch (proc_bind) {286  case omp_proc_bind_false:287    pb = "False";288    break;289  case omp_proc_bind_true:290    pb = "True";291    break;292  case omp_proc_bind_master:293    pb = "Master (Primary)";294    break;295  case omp_proc_bind_close:296    pb = "Close";297    break;298  case omp_proc_bind_spread:299    pb = "Spread";300    break;301  default:302    pb = "(Unknown Proc Bind Type)";303    break;304  }305  if (proc_bind == omp_proc_bind_spread || proc_bind == omp_proc_bind_close) {306    if (T <= P) {307      fprintf(stderr, "%s : (T(%d) <= P(%d)) : ", pb, T, P);308    } else {309      fprintf(stderr, "%s : (T(%d) > P(%d)) : ", pb, T, P);310    }311  } else {312    fprintf(stderr, "%s : T = %d, P = %d : ", pb, T, P);313  }314  vfprintf(stderr, format, args);315  va_end(args);316 317  exit(EXIT_FAILURE);318}319 320// Return 1 on failure, 0 on success.321static void proc_bind_check(omp_proc_bind_t proc_bind,322                            const place_list_t *parent, place_list_t **children,323                            int nchildren) {324  place_list_t *partition;325  int T, i, j, place, low, high, first, last, count, current_place, num_places;326  const int *place_nums;327  int P = parent->num_places;328 329  // Find the correct T (there could be null entries in children)330  place_list_t **partitions =331      (place_list_t **)malloc(sizeof(place_list_t *) * nchildren);332  T = 0;333  for (i = 0; i < nchildren; ++i)334    if (children[i])335      partitions[T++] = children[i];336  // Only able to check spread, close, master (primary)337  if (proc_bind != omp_proc_bind_spread && proc_bind != omp_proc_bind_close &&338      proc_bind != omp_proc_bind_master)339    proc_bind_die(proc_bind, T, P, NULL, NULL,340                  "Cannot check this proc bind type\n");341 342  if (proc_bind == omp_proc_bind_spread) {343    if (T <= P) {344      // Run through each subpartition345      for (i = 0; i < T; ++i) {346        partition = partitions[i];347        place_nums = partition->place_nums;348        num_places = partition->num_places;349        current_place = partition->current_place;350        // Correct count?351        low = P / T;352        high = P / T + (P % T ? 1 : 0);353        if (num_places != low && num_places != high) {354          proc_bind_die(proc_bind, T, P,355                        "Incorrect number of places for thread %d: %d. "356                        "Expecting between %d and %d\n",357                        i, num_places, low, high);358        }359        // Consecutive places?360        for (j = 1; j < num_places; ++j) {361          if (place_nums[j] != (place_nums[j - 1] + 1) % P) {362            proc_bind_die(proc_bind, T, P,363                          "Not consecutive places: %d, %d in partition\n",364                          place_nums[j - 1], place_nums[j]);365          }366        }367        first = place_nums[0];368        last = place_nums[num_places - 1];369        // Primary thread executes on place of the parent thread?370        if (i == 0) {371          if (current_place != parent->current_place) {372            proc_bind_die(373                proc_bind, T, P,374                "Primary thread not on same place (%d) as parent thread (%d)\n",375                current_place, parent->current_place);376          }377        } else {378          // Thread's current place is first place within it's partition?379          if (current_place != first) {380            proc_bind_die(proc_bind, T, P,381                          "Thread's current place (%d) is not the first place "382                          "in its partition [%d, %d]\n",383                          current_place, first, last);384          }385        }386        // Partitions don't have intersections?387        int f1 = first;388        int l1 = last;389        for (j = 0; j < i; ++j) {390          int f2 = partitions[j]->place_nums[0];391          int l2 = partitions[j]->place_nums[partitions[j]->num_places - 1];392          if (f1 > l1 && f2 > l2) {393            proc_bind_die(proc_bind, T, P,394                          "partitions intersect. [%d, %d] and [%d, %d]\n", f1,395                          l1, f2, l2);396          }397          if (f1 > l1 && f2 <= l2)398            if (f1 < l2 || l1 > f2) {399              proc_bind_die(proc_bind, T, P,400                            "partitions intersect. [%d, %d] and [%d, %d]\n", f1,401                            l1, f2, l2);402            }403          if (f1 <= l1 && f2 > l2)404            if (f2 < l1 || l2 > f1) {405              proc_bind_die(proc_bind, T, P,406                            "partitions intersect. [%d, %d] and [%d, %d]\n", f1,407                            l1, f2, l2);408            }409          if (f1 <= l1 && f2 <= l2)410            if (!(f2 > l1 || l2 < f1)) {411              proc_bind_die(proc_bind, T, P,412                            "partitions intersect. [%d, %d] and [%d, %d]\n", f1,413                            l1, f2, l2);414            }415        }416      }417    } else {418      // T > P419      // Each partition has only one place?420      for (i = 0; i < T; ++i) {421        if (partitions[i]->num_places != 1) {422          proc_bind_die(423              proc_bind, T, P,424              "Incorrect number of places for thread %d: %d. Expecting 1\n", i,425              partitions[i]->num_places);426        }427      }428      // Correct number of consecutive threads per partition?429      low = T / P;430      high = T / P + (T % P ? 1 : 0);431      for (i = 1, count = 1; i < T; ++i) {432        if (partitions[i]->place_nums[0] == partitions[i - 1]->place_nums[0]) {433          count++;434          if (count > high) {435            proc_bind_die(436                proc_bind, T, P,437                "Too many threads have place %d for their partition\n",438                partitions[i]->place_nums[0]);439          }440        } else {441          if (count < low) {442            proc_bind_die(443                proc_bind, T, P,444                "Not enough threads have place %d for their partition\n",445                partitions[i]->place_nums[0]);446          }447          count = 1;448        }449      }450      // Primary thread executes on place of the parent thread?451      current_place = partitions[0]->place_nums[0];452      if (parent->current_place != -1 &&453          current_place != parent->current_place) {454        proc_bind_die(455            proc_bind, T, P,456            "Primary thread not on same place (%d) as parent thread (%d)\n",457            current_place, parent->current_place);458      }459    }460  } else if (proc_bind == omp_proc_bind_close ||461             proc_bind == omp_proc_bind_master) {462    // Check that each subpartition is the same as the parent463    for (i = 0; i < T; ++i) {464      partition = partitions[i];465      place_nums = partition->place_nums;466      num_places = partition->num_places;467      current_place = partition->current_place;468      if (parent->num_places != num_places) {469        proc_bind_die(proc_bind, T, P,470                      "Number of places in subpartition (%d) does not match "471                      "parent (%d)\n",472                      num_places, parent->num_places);473      }474      for (j = 0; j < num_places; ++j) {475        if (parent->place_nums[j] != place_nums[j]) {476          proc_bind_die(proc_bind, T, P,477                        "Subpartition place (%d) does not match "478                        "parent partition place (%d)\n",479                        place_nums[j], parent->place_nums[j]);480        }481      }482    }483    // Find index into place_nums of current place for parent484    for (j = 0; j < parent->num_places; ++j)485      if (parent->place_nums[j] == parent->current_place)486        break;487    if (proc_bind == omp_proc_bind_close) {488      if (T <= P) {489        // close T <= P490        // check place assignment for each thread491        for (i = 0; i < T; ++i) {492          partition = partitions[i];493          current_place = partition->current_place;494          if (current_place != parent->place_nums[j]) {495            proc_bind_die(496                proc_bind, T, P,497                "Thread %d's current place (%d) is incorrect. expected %d\n", i,498                current_place, parent->place_nums[j]);499          }500          j = (j + 1) % parent->num_places;501        }502      } else {503        // close T > P504        // check place assignment for each thread505        low = T / P;506        high = T / P + (T % P ? 1 : 0);507        count = 1;508        if (partitions[0]->current_place != parent->current_place) {509          proc_bind_die(510              proc_bind, T, P,511              "Primary thread's place (%d) is not parent thread's place (%d)\n",512              partitions[0]->current_place, parent->current_place);513        }514        for (i = 1; i < T; ++i) {515          current_place = partitions[i]->current_place;516          if (current_place == parent->place_nums[j]) {517            count++;518            if (count > high) {519              proc_bind_die(520                  proc_bind, T, P,521                  "Too many threads have place %d for their current place\n",522                  current_place);523            }524          } else {525            if (count < low) {526              proc_bind_die(527                  proc_bind, T, P,528                  "Not enough threads have place %d for their current place\n",529                  parent->place_nums[j]);530            }531            j = (j + 1) % parent->num_places;532            if (current_place != parent->place_nums[j]) {533              proc_bind_die(534                  proc_bind, T, P,535                  "Thread %d's place (%d) is not corret. Expected %d\n", i,536                  partitions[i]->current_place, parent->place_nums[j]);537            }538            count = 1;539          }540        }541      }542    } else {543      // proc_bind_primary544      // Every thread should be assigned to the primary thread's place545      for (i = 0; i < T; ++i) {546        if (partitions[i]->current_place != parent->current_place) {547          proc_bind_die(548              proc_bind, T, P,549              "Thread %d's place (%d) is not the primary thread's place (%d)\n",550              i, partitions[i]->current_place, parent->current_place);551        }552      }553    }554  }555 556  // Check that each partition's current place is within the partition557  for (i = 0; i < T; ++i) {558    current_place = partitions[i]->current_place;559    num_places = partitions[i]->num_places;560    first = partitions[i]->place_nums[0];561    last = partitions[i]->place_nums[num_places - 1];562    for (j = 0; j < num_places; ++j)563      if (partitions[i]->place_nums[j] == current_place)564        break;565    if (j == num_places) {566      proc_bind_die(proc_bind, T, P,567                    "Thread %d's current place (%d) is not within its "568                    "partition [%d, %d]\n",569                    i, current_place, first, last);570    }571  }572 573  free(partitions);574}575 576#endif577