aboutsummaryrefslogtreecommitdiff
path: root/base_bench.c
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-04-08 14:41:33 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-04-08 14:41:33 +0200
commit1a7efd92e7caee5dc6493190ba0915dfd88fa7bb (patch)
tree547b7604889dc8865e4daac1fcb6474fc624f1fc /base_bench.c
parentbde04c0f3399d40f8e0499328f61613d36deced6 (diff)
Benchmark framework
Diffstat (limited to 'base_bench.c')
-rw-r--r--base_bench.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/base_bench.c b/base_bench.c
new file mode 100644
index 0000000..52ab639
--- /dev/null
+++ b/base_bench.c
@@ -0,0 +1,148 @@
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "src/std/memory.h"
+#include "src/std/time.h"
+#include "src/std/types.h"
+
+#define BENCH_DURATION_MS 100.0f
+
+typedef struct {
+ f32 ns;
+ f32 stddev;
+ size peak;
+ size net;
+ f32 total_ms;
+} BenchResult;
+
+typedef void (*BenchSetup)(Arena *a);
+typedef i32 (*BenchFn)(Arena *a);
+
+static inline BenchResult bench_run(BenchSetup setup, BenchFn fn) {
+ Arena scratch = Arena_new(32 * 1024 * 1024);
+ u64 calls = 0;
+ size peak = 0;
+ size net = 0;
+ double mean = 0, m2 = 0;
+ double total_ns = 0;
+
+ Time wall_start = Time_get();
+ Duration limit = Duration_from_ms(BENCH_DURATION_MS);
+ u64 limit_ns = limit.ns + limit.s * (u64)1e9;
+
+ for (;;) {
+ Time wall_now = Time_get();
+ Duration wall_elapsed = Time_diff(wall_now, wall_start);
+ if (wall_elapsed.s > 0 ||
+ wall_elapsed.ns >= limit_ns)
+ break;
+
+ Arena_reset(&scratch);
+ setup(&scratch);
+ size before = Arena_get_used(scratch);
+ Time start = Time_get();
+ fn(&scratch);
+ Time end = Time_get();
+ size after = Arena_get_used(scratch);
+ Duration d = Time_diff(end, start);
+ double sample = (double)d.ns + (double)d.s * 1e9;
+ total_ns += sample;
+ calls++;
+ double delta = sample - mean;
+ mean += delta / (double)calls;
+ m2 += delta * (sample - mean);
+ if (after > peak) peak = after;
+ net = after - before;
+ }
+
+ if (calls == 0) calls = 1;
+ return (BenchResult){
+ .ns = (f32)(total_ns / (double)calls),
+ .stddev = calls > 1 ? (f32)sqrt(m2 / (double)(calls - 1)) : 0,
+ .peak = peak,
+ .net = net,
+ .total_ms = (f32)(total_ns / 1e6),
+ };
+}
+
+#define MAX_RESULTS 64
+typedef struct {
+ const char *name;
+ BenchResult result;
+} BenchEntry;
+static BenchEntry _bench_registry[MAX_RESULTS];
+static i32 _bench_count = 0;
+
+static void _bench_register(const char *name, BenchResult r) {
+ if (_bench_count < MAX_RESULTS) {
+ _bench_registry[_bench_count].name = name;
+ _bench_registry[_bench_count].result = r;
+ _bench_count++;
+ }
+}
+
+static BenchResult *_bench_find(const char *name) {
+ for (i32 i = 0; i < _bench_count; i++)
+ if (strcmp(_bench_registry[i].name, name) == 0)
+ return &_bench_registry[i].result;
+ return NULL;
+}
+
+static void _fmt_mem(char *buf, size_t bufsz, size bytes) {
+ if (bytes >= 1024 * 1024)
+ snprintf(buf, bufsz, "%.1f MB", (double)bytes / (1024 * 1024));
+ else if (bytes >= 1024)
+ snprintf(buf, bufsz, "%.1f KB", (double)bytes / 1024);
+ else
+ snprintf(buf, bufsz, "%zu B", bytes);
+}
+
+static void _fmt_ratio(char *buf, size_t bufsz, double val, double ref) {
+ if (ref == 0) {
+ buf[0] = '\0';
+ return;
+ }
+ double ratio = val / ref;
+ if (ratio >= 1.005)
+ snprintf(buf, bufsz, "(x%.0f)", ratio);
+ else if (ratio < 0.995)
+ snprintf(buf, bufsz, "(x%.2f)", ratio);
+ else
+ snprintf(buf, bufsz, "(x1)");
+}
+
+static void _bench_print_row(const char *name, BenchResult r,
+ const char *compare) {
+ BenchResult *ref = compare ? _bench_find(compare) : NULL;
+
+ char ns_ratio[16] = "", peak_ratio[16] = "", net_ratio[16] = "";
+ if (ref) {
+ _fmt_ratio(ns_ratio, sizeof(ns_ratio), r.ns, ref->ns);
+ _fmt_ratio(peak_ratio, sizeof(peak_ratio), (double)r.peak,
+ (double)ref->peak);
+ _fmt_ratio(net_ratio, sizeof(net_ratio), (double)r.net,
+ (double)ref->net);
+ }
+
+ char peak[16], net[16];
+ _fmt_mem(peak, sizeof(peak), r.peak);
+ _fmt_mem(net, sizeof(net), r.net);
+
+ printf(" %-38s %10.0f ±%7.0f %-6s %10s %-6s %10s %-6s %6.0f ms\n",
+ name, r.ns, r.stddev, ns_ratio, peak, peak_ratio, net, net_ratio,
+ r.total_ms);
+}
+
+static void bench_group(const char *label) {
+ printf("\n[%s]\n", label);
+ printf(" %-38s %10s %-14s %10s %-6s %10s %-6s %9s\n", "name",
+ "ns/call", "± stddev", "peak mem", "", "net mem", "", "total");
+}
+
+#define bench(name, setup, func, compare) \
+ do { \
+ BenchResult _r = bench_run(setup, func); \
+ _bench_register(name, _r); \
+ _bench_print_row(name, _r, compare); \
+ } while (0)