commit 2064b054b1037824f2ad243f9dd86ae6c03f8a14
parent dd4e201390262f46d2b91b9815706a82561eb3d9
Author: robert <robertrussell.72001@gmail.com>
Date: Wed, 10 Aug 2022 12:05:06 -0700
Add benchmarking module
Diffstat:
4 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -4,6 +4,7 @@ include config.mk
SRC =\
src/alloc.c\
+ src/bench.c\
src/log.c\
src/opt.c\
src/str.c\
@@ -16,9 +17,10 @@ libcext.a: $(SRC:.c=.o)
$(CC) -c -o $@ $(CFLAGS) $<
src/alloc.o: src/alloc.c inc/cext/cext.h inc/cext/def.h inc/cext/alloc.h inc/cext/log.h config.mk
+src/bench.o: src/bench.c inc/cext/cext.h inc/cext/def.h inc/cext/bench.h inc/cext/log.h config.mk
src/log.o: src/log.c inc/cext/cext.h inc/cext/def.h inc/cext/log.h config.mk
src/opt.o: src/opt.c inc/cext/cext.h inc/cext/def.h inc/cext/opt.h config.mk
-src/str.o: src/str.c inc/cext/cext.h inc/cext/def.h inc/cext/str.h config.mk
+src/str.o: src/str.c inc/cext/cext.h inc/cext/def.h inc/cext/str.h config.mk # TODO missing deps
src/utf8.o: src/utf8.c inc/cext/cext.h inc/cext/def.h inc/cext/utf8.h config.mk
clean:
diff --git a/inc/cext/all.h b/inc/cext/all.h
@@ -1,3 +1,4 @@
+/* Everything except bench.h */
#include "cext/alloc.h"
#include "cext/cext.h"
#include "cext/deque.h"
diff --git a/inc/cext/bench.h b/inc/cext/bench.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "cext/def.h"
+
+/*
+Usage:
+ void my_benchmark(u64 N) {
+ <initialization (not timed)>
+ bench_start();
+ for (u64 i = 0; i < N; i++) {
+ <code to benchmark>
+ }
+ bench_stop();
+ <cleanup (not timed)>
+ }
+ int main(void) {
+ bench("my benchmark", my_benchmark, 3*SECONDS);
+ }
+Note that <code to benchmark> can contain calls to bench_stop and bench_start
+to pause and restart timing.
+*/
+
+#define MILLISECONDS 1000000ULL
+#define SECONDS 1000000000ULL
+
+void bench(char *name, void (*fn)(u64 N), u64 goalns);
+void bench_start(void);
+void bench_stop(void);
diff --git a/src/bench.c b/src/bench.c
@@ -0,0 +1,99 @@
+#define _POSIX_C_SOURCE 199309L /* clock_gettime */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "cext/bench.h"
+#include "cext/cext.h"
+#include "cext/log.h"
+
+#ifndef _POSIX_THREAD_CPUTIME
+#error "Need CLOCK_PROCESS_CPUTIME_ID"
+#endif
+
+#define MAXN 1000000000ULL
+
+static u64 run(void (*fn)(u64 N), u64 N);
+static u64 requiredN(u64 prevN, u64 prevns, u64 goalns);
+static void printnsop(u64 N, u64 ns);
+
+static bool started; /* Has bench_start been called? */
+static bool active; /* Is the timer currently on? */
+static struct timespec start;
+static u64 accumns;
+
+u64
+run(void (*fn)(u64 N), u64 N) {
+ started = active = false;
+ accumns = 0;
+ fn(N);
+ if (active || !started)
+ fatalf("bench misuse");
+ return accumns;
+}
+
+u64
+requiredN(u64 prevN, u64 prevns, u64 goalns) {
+ /* This is pretty much copied from Go's testing package. */
+ u64 N = prevN * goalns / (prevns == 0 ? 1 : prevns);
+ N += N/5; /* Overestimate by 1.2x. */
+ N = MIN(N, 100*prevN); /* Grow slowly, in case prevns is inaccurate. */
+ N = MAX(N, prevN+1); /* Do at least one more run, */
+ N = MIN(N, MAXN); /* but don't do too many. */
+ return N;
+}
+
+void
+printnsop(u64 N, u64 ns) {
+ double nsop = (double) ns / N;
+
+ /* This is pretty much copied from Go's testing package. */
+ char *format;
+ if (nsop == 0 || nsop >= 99.95) format = "%10.0f ns/op";
+ else if (nsop >= 9.995) format = "%12.1f ns/op";
+ else if (nsop >= 0.9995) format = "%13.2f ns/op";
+ else if (nsop >= 0.09995) format = "%14.3f ns/op";
+ else if (nsop >= 0.009995) format = "%15.4f ns/op";
+ else if (nsop >= 0.0009995) format = "%16.5f ns/op";
+ else format = "%17.6f ns/op";
+
+ fprintf(stderr, format, nsop);
+}
+
+void
+bench(char *name, void (*fn)(u64 N), u64 goalns) {
+ run(fn, 1); /* Warmup */
+ u64 N = 1;
+ u64 ns;
+ while ((ns = run(fn, N)) < goalns && N < MAXN)
+ N = requiredN(N, ns, goalns);
+
+ fprintf(stderr, "benchmark: %-25s%10"PRId64" iters ", name, N);
+ printnsop(N, ns);
+ fprintf(stderr, "\n");
+}
+
+void
+bench_start(void) {
+ if (active)
+ return;
+ active = true;
+ started = true;
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start) < 0)
+ fatalf("clock_gettime: %s", strerror(errno));
+}
+
+void
+bench_stop(void) {
+ if (!active)
+ return;
+ struct timespec stop;
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &stop) < 0)
+ fatalf("clock_gettime: %s", strerror(errno));
+ active = false;
+ accumns += (stop.tv_sec - start.tv_sec) * 1000000000ULL
+ + (stop.tv_nsec - start.tv_nsec);
+}