/* runt: run a command N times, report min/median/max wall time and median * mean user/sys CPU of the child, in milliseconds. C89 for xlc on AIX 4.3.3. * Usage: runt N [-q] cmd [args...] (-q: send the child's stdout to /dev/null) * Claude Opus 5.5, 2026-10-09. */ #include #include #include #include #include #include #include #include #include static double tv_ms(struct timeval *a, struct timeval *b) { return (b->tv_sec - a->tv_sec) * 1000.0 + (b->tv_usec - a->tv_usec) / 1000.0; } static int cmp(const void *a, const void *b) { double x = *(const double *)a, y = *(const double *)b; return (x < y) ? -1 : (x > y); } int main(int argc, char **argv) { int n, i, quiet = 0, argi = 2; double *wall, *usr, *sys; struct timeval t0, t1; struct rusage r0, r1; pid_t pid; int status; if (argc < 3) { fprintf(stderr, "usage: runt N [-q] cmd [args]\n"); return 2; } n = atoi(argv[1]); if (strcmp(argv[2], "-q") == 0) { quiet = 1; argi = 3; } wall = (double *)malloc(n * sizeof(double)); usr = (double *)malloc(n * sizeof(double)); sys = (double *)malloc(n * sizeof(double)); for (i = 0; i < n; i++) { getrusage(RUSAGE_CHILDREN, &r0); gettimeofday(&t0, NULL); pid = fork(); if (pid == 0) { if (quiet) { int fd = open("/dev/null", O_WRONLY); dup2(fd, 1); } execvp(argv[argi], argv + argi); _exit(127); } waitpid(pid, &status, 0); gettimeofday(&t1, NULL); getrusage(RUSAGE_CHILDREN, &r1); wall[i] = tv_ms(&t0, &t1); usr[i] = tv_ms(&r0.ru_utime, &r1.ru_utime); sys[i] = tv_ms(&r0.ru_stime, &r1.ru_stime); } qsort(wall, n, sizeof(double), cmp); /* AIX charges CPU in 10 ms ticks, so per-run CPU is useless; report means */ { double su = 0, ss = 0; for (i = 0; i < n; i++) { su += usr[i]; ss += sys[i]; } printf("n=%d wall min %.2f med %.2f max %.2f cpu mean usr %.2f sys %.2f (ms)\n", n, wall[0], wall[n / 2], wall[n - 1], su / n, ss / n); } return 0; }