Bench: GC overhead via bump-allocator comparison
Adds --alloc=<gc|bump> to ail build/run. Bump path links a 256MB no-free arena C stub instead of libgc; IR is byte-identical except for the @GC_malloc → @bump_malloc symbol swap. Bench harness times two allocation-heavy workloads (list cons/sum and balanced tree build/walk) under both modes. Numbers (RUNS=5, median of 4): bench_list_sum gc 0.141s bump 0.048s +194% bench_tree_walk gc 0.103s bump 0.041s +151% Bucket: large. ~60% of runtime is Boehm on these workloads — upper bound for any realistic program. Both fixtures hold the heap fully live, so the cost we're seeing is Boehm's allocate path itself, not collection work; that fact narrows the design space for the GC discussion. - crates/ailang-codegen: AllocStrategy enum, three callsites and the IR header parameterised. - crates/ail/src/main.rs: --alloc flag plumbed; bump runtime located + compiled on demand. - runtime/bump.c: 256MB static arena, abort-on-overflow. - examples/bench_list_sum, bench_tree_walk: accumulator-form fixtures (textbook recursive sum was constructor-blocked). - bench/run.sh: harness with Python timing helper (Arch's /usr/bin/time isn't part of the base install). No language-level changes; default --alloc=gc, all 141 workspace tests green, all 5 IR snapshots unchanged, 11 prior fixtures produce identical stdout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
/* Bench-only bump allocator stub.
|
||||
*
|
||||
* Used by `ail build --alloc=bump` (Bench iter) to A/B compare AILang
|
||||
* binaries against the default Boehm-GC build. The whole runtime is a
|
||||
* 256 MB statically-allocated arena and a single bump pointer; there
|
||||
* is no `free`, no scan, no anything. If the workload exceeds 256 MB
|
||||
* we abort — this is bench code, the right response to overflow is to
|
||||
* notice and pick a smaller workload.
|
||||
*
|
||||
* The signature mirrors `GC_malloc` from libgc: `void *bump_malloc(size_t)`.
|
||||
* The codegen replaces every `call ptr @GC_malloc` with
|
||||
* `call ptr @bump_malloc` when `--alloc=bump` is set, so the AILang IR
|
||||
* is otherwise byte-identical between the two strategies.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ARENA_BYTES (256ul * 1024ul * 1024ul)
|
||||
|
||||
static uint8_t arena[ARENA_BYTES];
|
||||
static size_t cursor = 0;
|
||||
|
||||
void *bump_malloc(size_t n) {
|
||||
/* Align bump pointer up to 8 bytes — AILang's allocations are all
|
||||
* 8-byte aligned (tag + 8-byte fields, env pointers, closure pairs
|
||||
* of two `ptr`s). Matches the alignment Boehm gives us. */
|
||||
size_t aligned = (cursor + 7ul) & ~((size_t)7ul);
|
||||
if (aligned + n > ARENA_BYTES) {
|
||||
fprintf(stderr,
|
||||
"bump_malloc: arena exhausted (cursor=%zu, requested=%zu, arena=%zu)\n",
|
||||
aligned, n, (size_t)ARENA_BYTES);
|
||||
abort();
|
||||
}
|
||||
void *p = (void *)(arena + aligned);
|
||||
cursor = aligned + n;
|
||||
return p;
|
||||
}
|
||||
Reference in New Issue
Block a user