diff options
Diffstat (limited to 'base_bench.c')
| -rw-r--r-- | base_bench.c | 148 |
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) |
