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 }