#define _GNU_SOURCE
#include <omp.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
static double now_us(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return t.tv_sec * 1e6 + t.tv_nsec / 1e3;
}
static inline void relax(void) {
#if defined(__x86_64__) || defined(__i386__)
__builtin_ia32_pause();
#elif defined(__aarch64__)
__asm__ __volatile__("yield");
#endif
}
static void busy(double us) {
double end = now_us() + us;
while (now_us() < end) relax();
}
static int cmp(const void *a, const void *b) {
double x = *(const double *)a, y = *(const double *)b;
return (x > y) - (x < y);
}
static FILE *csv;
static char prefix[512];
static void row(const char *scenario, int width, const char *param, const char *metric, double *v,
int n, double ideal) {
qsort(v, n, sizeof *v, cmp);
double med = v[n / 2], p90 = v[(n * 9 / 10) < n ? n * 9 / 10 : n - 1], max = v[n - 1];
printf("%-9s %-10s %5d %-22s %-7s %11.1f %11.1f %11.1f %10.1f\n", scenario, "libgomp", width,
param, metric, med, p90, max, med - ideal);
if (csv)
fprintf(csv, "%s,%s,libgomp,%d,%s,%s,%.2f,%.2f,%.2f,%.2f\n", prefix, scenario, width, param,
metric, med, p90, max, med - ideal);
}
static void call(int width, int sections, double work, double *first, double *whole) {
double t0 = now_us();
for (int s = 0; s < sections; s++) {
#pragma omp parallel num_threads(width)
busy(work);
if (s == 0) *first = now_us() - t0;
}
*whole = now_us() - t0;
}
static pid_t load_children[256];
static int load_n;
static void load_start(const char *spec) {
int k = 0, p = 0;
if (!spec || sscanf(spec, "%d:%d", &k, &p) != 2) return;
for (int i = 0; i < k && i < 256; i++) {
pid_t c = fork();
if (c == 0) {
for (;;) {
double t = now_us();
while (now_us() - t < 100.0 * p) relax();
usleep((useconds_t)(10000 - 100 * p > 0 ? 10000 - 100 * p : 0));
}
}
if (c > 0) load_children[load_n++] = c;
}
usleep(200000);
}
static void load_stop(void) {
for (int i = 0; i < load_n; i++) {
kill(load_children[i], SIGKILL);
waitpid(load_children[i], NULL, 0);
}
}
static int want(const char *s) {
const char *w = getenv("WAIT_OMP_SCENARIO");
return !w || !strcmp(w, "all") || !strcmp(w, s);
}
int main(int argc, char **argv) {
if (argc < 3) {
fprintf(stderr, "usage: wait_omp <width> <calls> [csv] [label]\n");
return 2;
}
int width = atoi(argv[1]), calls = atoi(argv[2]);
cpu_set_t set;
int cpus = sched_getaffinity(0, sizeof set, &set) == 0 ? CPU_COUNT(&set) : 0;
char host[256] = "";
gethostname(host, sizeof host - 1);
const char *label = argc > 4 ? argv[4] : "";
const char *load_spec = getenv("WAIT_OMP_LOAD");
char load_text[64];
int lk = 0, lp = 0;
if (load_spec && sscanf(load_spec, "%d:%d", &lk, &lp) == 2)
snprintf(load_text, sizeof load_text, "%dx%d%%", lk, lp);
else
snprintf(load_text, sizeof load_text, "%s",
getenv("WAIT_BENCH_LOAD") ? getenv("WAIT_BENCH_LOAD") : "none");
const char *load = load_text;
snprintf(prefix, sizeof prefix, "%s,%s,%d,%s", host, label, cpus, load);
if (argc > 3) {
int fresh = access(argv[3], F_OK) != 0;
csv = fopen(argv[3], "a");
if (csv && fresh)
fprintf(csv, "host,label,cpus,load,scenario,impl,width,param,metric,median_us,p90_us,max_us,overhead_us\n");
}
double *a = malloc(sizeof(double) * calls), *b = malloc(sizeof(double) * calls);
char param[64];
load_start(load_spec);
for (int i = 0; i < 10; i++) call(width, 10, 5, a, b);
int works[3] = {5, 20, 50};
for (int k = 0; k < 3 && want("sections"); k++) {
for (int i = 0; i < calls; i++) call(width, 200, works[k], &a[i], &b[i]);
snprintf(param, sizeof param, "work=%dus,sections=200", works[k]);
row("sections", width, param, "call", b, calls, 200.0 * works[k]);
}
int gaps[4] = {0, 100, 1000, 10000};
for (int k = 0; k < 4 && want("gaps"); k++) {
for (int i = 0; i < calls / 2; i++) {
if (gaps[k]) usleep(gaps[k]);
call(width, 10, 5, &a[i], &b[i]);
}
snprintf(param, sizeof param, "gap=%dus", gaps[k]);
row("gaps", width, param, "first", a, calls / 2, 5);
row("gaps", width, param, "call", b, calls / 2, 50);
}
if (want("acquire")) {
for (int i = 0; i < calls / 2; i++) call(width, 50, 20, &a[i], &b[i]);
row("acquire", width, "held_team", "call", b, calls / 2, 1000);
}
load_stop();
if (csv) fclose(csv);
return 0;
}