rcx

miscellaneous C library
git clone git://git.rr3.xyz/rcx
Log | Files | Refs | README | LICENSE

bench.c (2710B)


      1 #define _POSIX_C_SOURCE 199309L /* clock_gettime */
      2 
      3 #include <errno.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <time.h>
      7 #include <unistd.h>
      8 
      9 #include "bench.h"
     10 #include "log.h"
     11 #include "rcx.h"
     12 
     13 #ifndef _POSIX_THREAD_CPUTIME
     14 #error "Need CLOCK_PROCESS_CPUTIME_ID"
     15 #endif
     16 
     17 #define MAXN 10000000000ULL
     18 
     19 static u64 run(void (*fn)(u64 N), u64 N);
     20 static u64 requiredN(u64 prevN, u64 prevns, u64 goalns);
     21 static void printnsop(u64 N, u64 ns);
     22 
     23 static bool started; /* Has r_bench_start been called? */
     24 static bool active;  /* Is the timer currently on? */
     25 static struct timespec start;
     26 static u64 accumns;
     27 
     28 u64
     29 run(void (*fn)(u64 N), u64 N) {
     30 	started = active = false;
     31 	accumns = 0;
     32 	fn(N);
     33 	if (active || !started)
     34 		r_fatalf("bench misuse");
     35 	return accumns;
     36 }
     37 
     38 u64
     39 requiredN(u64 prevN, u64 prevns, u64 goalns) {
     40 	/* This is pretty much copied from Go's testing package. */
     41 	u64 N = prevN * goalns / (prevns == 0 ? 1 : prevns);
     42 	N += N/5;              /* Overestimate by 1.2x. */
     43 	N = MIN(N, 100*prevN); /* Grow slowly, in case prevns is inaccurate. */
     44 	N = MAX(N, prevN+1);   /* Do at least one more run, */
     45 	N = MIN(N, MAXN);      /* but don't do too many. */
     46 	return N;
     47 }
     48 
     49 void
     50 printnsop(u64 N, u64 ns) {
     51 	double nsop = (double) ns / N;
     52 
     53 	/* This is pretty much copied from Go's testing package. */
     54 	char *format;
     55 	if (nsop == 0 || nsop >= 99.95) format = "%10.0f ns/op";
     56 	else if (nsop >= 9.995)         format = "%12.1f ns/op";
     57 	else if (nsop >= 0.9995)        format = "%13.2f ns/op";
     58 	else if (nsop >= 0.09995)       format = "%14.3f ns/op";
     59 	else if (nsop >= 0.009995)      format = "%15.4f ns/op";
     60 	else if (nsop >= 0.0009995)     format = "%16.5f ns/op";
     61 	else                            format = "%17.6f ns/op";
     62 
     63 	fprintf(stderr, format, nsop);
     64 }
     65 
     66 void
     67 r_bench_(void (*fn)(u64 N), u32 goalms, char *name, ...) {
     68 	run(fn, 1); /* Warmup */
     69 	u64 goalns = (u64)goalms * U64_C(1000000);
     70 	u64 N = 1;
     71 	u64 ns;
     72 	while ((ns = run(fn, N)) < goalns && N < MAXN)
     73 		N = requiredN(N, ns, goalns);
     74 
     75 	char buf[1024];
     76 	va_list args;
     77 	va_start(args, name);
     78 	vsnprintf(buf, sizeof buf, name, args);
     79 	va_end(args);
     80 	fprintf(stderr, "benchmark: %-25s%10"PRIu64" iters  ", buf, N);
     81 	printnsop(N, ns);
     82 	fprintf(stderr, "\n");
     83 }
     84 
     85 void
     86 r_bench_start(void) {
     87 	if (active)
     88 		return;
     89 	active = true;
     90 	started = true;
     91 	if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start) < 0)
     92 		r_fatalf("clock_gettime: %s", strerror(errno));
     93 }
     94 
     95 void
     96 r_bench_stop(void) {
     97 	if (!active)
     98 		return;
     99 	struct timespec stop;
    100 	if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &stop) < 0)
    101 		r_fatalf("clock_gettime: %s", strerror(errno));
    102 	active = false;
    103 	accumns += (stop.tv_sec - start.tv_sec) * 1000000000ULL
    104 		+ (stop.tv_nsec - start.tv_nsec);
    105 }