/* * iobench.c - small file-system I/O benchmark for obsidian (AIX 4.3.3, C89). * Claude Opus 5.5, 2026-10-11. * * iobench seqw FILE MB [BS] write MB megabytes in BS-byte writes, then fsync * iobench seqr FILE [BS] read FILE start to end in BS-byte reads * iobench rand BS N NPROC FILE... N random BS-byte reads in total, split over NPROC * processes; process i reads FILE[i % nfiles] * iobench sync FILE N [BS] N appends of BS bytes, fsync after each * * Times are wall-clock (gettimeofday). rand and sync also print per-operation * latency quantiles. No /dev/urandom on AIX 4.3: offsets come from an LCG seeded * with pid and time. */ #include #include #include #include #include #include #include #include #include #include static double now(void) { struct timeval tv; gettimeofday(&tv, (void *)0); return tv.tv_sec + tv.tv_usec / 1e6; } static unsigned long seed; static unsigned long rnd(void) { seed = seed * 1103515245UL + 12345UL; return (seed >> 8) & 0xffffffUL; } static int cmpd(const void *a, const void *b) { double x = *(const double *)a, y = *(const double *)b; return x < y ? -1 : x > y; } static void quant(const char *tag, double *t, long n) { qsort(t, n, sizeof(double), cmpd); printf("%s n=%ld min=%.2f p10=%.2f median=%.2f p90=%.2f max=%.2f ms\n", tag, n, t[0] * 1e3, t[n / 10] * 1e3, t[n / 2] * 1e3, t[n * 9 / 10] * 1e3, t[n - 1] * 1e3); } static char *mkbuf(long bs) { char *b = malloc(bs); long i; if (!b) { perror("malloc"); exit(1); } for (i = 0; i < bs; i++) b[i] = (char)(i * 7 + 1); return b; } static int seqw(const char *f, long mb, long bs) { char *b = mkbuf(bs); long n = mb * 1048576L / bs, i; double t0, t1, t2; int fd = open(f, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror(f); return 1; } t0 = now(); for (i = 0; i < n; i++) if (write(fd, b, bs) != bs) { perror("write"); return 1; } t1 = now(); fsync(fd); t2 = now(); close(fd); printf("seqw %s %ld MB bs=%ld: write %.2f s, fsync %.2f s, %.2f MB/s incl fsync\n", f, mb, bs, t1 - t0, t2 - t1, mb / (t2 - t0)); return 0; } static int seqr(const char *f, long bs) { char *b = mkbuf(bs); double t0, t1, tot = 0; long r; int fd = open(f, O_RDONLY); if (fd < 0) { perror(f); return 1; } t0 = now(); while ((r = read(fd, b, bs)) > 0) tot += r; t1 = now(); close(fd); printf("seqr %s %.0f MB bs=%ld: %.2f s, %.2f MB/s\n", f, tot / 1048576, bs, t1 - t0, tot / 1048576 / (t1 - t0)); return 0; } static int rand_child(const char *f, long bs, long n, int id) { char *b = mkbuf(bs); double *lat = malloc(n * sizeof(double)), t; struct stat st; long blocks, i; int fd = open(f, O_RDONLY); if (fd < 0 || !lat) { perror(f); return 1; } fstat(fd, &st); blocks = st.st_size / bs; seed = (unsigned long)getpid() * 2654435761UL + (unsigned long)time((void *)0); for (i = 0; i < n; i++) { long blk = (long)((rnd() * 4096.0 + (rnd() & 4095)) / 68719476736.0 * blocks); t = now(); if (lseek(fd, (off_t)blk * bs, SEEK_SET) < 0 || read(fd, b, bs) != bs) { perror("read"); return 1; } lat[i] = now() - t; } { char tag[64]; sprintf(tag, " proc%d %s", id, f); quant(tag, lat, n); } return 0; } static int randr(long bs, long n, int np, char **files, int nf) { double t0, t1; int i, st; t0 = now(); for (i = 0; i < np; i++) { pid_t p = fork(); if (p == 0) { fflush(stdout); exit(rand_child(files[i % nf], bs, n / np, i)); } } for (i = 0; i < np; i++) wait(&st); t1 = now(); printf("rand bs=%ld procs=%d files=%d: %ld reads in %.2f s = %.1f reads/s\n", bs, np, nf, n / np * np, t1 - t0, (n / np * np) / (t1 - t0)); return 0; } static int syncw(const char *f, long n, long bs) { char *b = mkbuf(bs); double *lat = malloc(n * sizeof(double)), t, t0, t1; long i; int fd = open(f, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0 || !lat) { perror(f); return 1; } t0 = now(); for (i = 0; i < n; i++) { t = now(); if (write(fd, b, bs) != bs) { perror("write"); return 1; } fsync(fd); lat[i] = now() - t; } t1 = now(); close(fd); printf("sync %s bs=%ld: %ld appends in %.2f s = %.1f per s\n", f, bs, n, t1 - t0, n / (t1 - t0)); quant(" sync", lat, n); return 0; } int main(int argc, char **argv) { setvbuf(stdout, (char *)0, _IOLBF, 0); if (argc >= 4 && !strcmp(argv[1], "seqw")) return seqw(argv[2], atol(argv[3]), argc > 4 ? atol(argv[4]) : 65536); if (argc >= 3 && !strcmp(argv[1], "seqr")) return seqr(argv[2], argc > 3 ? atol(argv[3]) : 65536); if (argc >= 6 && !strcmp(argv[1], "rand")) return randr(atol(argv[2]), atol(argv[3]), atoi(argv[4]), argv + 5, argc - 5); if (argc >= 4 && !strcmp(argv[1], "sync")) return syncw(argv[2], atol(argv[3]), argc > 4 ? atol(argv[4]) : 4096); fprintf(stderr, "usage: see source\n"); return 2; }