Files
AILang/runtime/bump.c
T
Brummel 65e280bb70 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>
2026-05-08 00:06:20 +02:00

41 lines
1.5 KiB
C

/* 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;
}