Root-cause fix for the residual crashes I had documented as known issues in §6.6.4. Every heap_alloc call site was setting its type byte with `orq $(HT_X << 8), -8(%rax)` — but OR merges with the stale type byte from a free-list-reused block. A pair previously used as a vector (type 5 = 0b101) re-allocated as pair (type 1 = 0b001) ends up with merged type 0b101 = still vector. Walker then treats the pair as a vector, reads the pair's car as a "length", and walks off the block end — hence the hash-set bench's "unbound variable: t", memory bench's "unbound variable: lst", arena bench's "unbound variable: k". Fix: overwrite the byte instead of OR-ing. 18 sites converted from `orq $(HT_X << 8), -8(%rax)` to `movb $HT_X, -7(%rax)`. Every previously-residual crash gone on first rerun. Refreshed benchmark numbers throughout §6.6: §6.6 Memory table: 122× less memory at 26% slowdown (was 124×, 30%). Range shifted because the fix also accelerated the common paths; ratio stable. §6.6.4 HTTP soak at 50,000 requests × 16 concurrent × 4 cells: no-GC + snapshot 630 req/s peak 100 KB growth 4 KB GC + snapshot 633 req/s peak 120 KB growth 4 KB no-GC + no snapshot 625 req/s peak 458 MB OOM at cap GC + no snapshot 610 req/s peak 1,092 KB growth 852 KB Cell 4 now sustains 50K requests with steady-state 1-chunk memory. Previous residual edge at 50K (cell 4 failing to start) was a manifestation of the same type-byte bug, now gone. §6.6.3 Adaptive numbers collapsed to within ~1% across all three workloads (was 6% / 7% / 17% deltas). Paper updated to honestly report adaptive as a null experiment on these shapes — neutral cost, same stats surface, default on. §6.6 diagram: bench-gc.png refreshed to match new numbers. 137 asm no-GC + 137 asm GC + 189 shared functional all pass. Hash-set / memory / arena / adaptive / HTTP benches all clean.
17 lines
793 B
Text
17 lines
793 B
Text
// GC control-group comparison — 2026-04-18
|
||
// "A diagram is worth 10,000 words." — russell@unturf.com
|
||
digraph bench_gc {
|
||
rankdir=LR
|
||
node [shape=record, style=filled, fontname="Helvetica", fontsize=11]
|
||
edge [style=invis]
|
||
label="Peak RSS — 2000 × build-list 200 + sum + discard (i5-8350U)"
|
||
labelloc=t
|
||
fontsize=14
|
||
fontname="Helvetica Bold"
|
||
|
||
bump [label="{Asm bump-only|peak RSS: 133.1 MB\nunbounded growth}" fillcolor="#e17055" fontcolor=white width=13.0]
|
||
gc [label="{Asm naive GC|peak RSS: 1.1 MB\nsteady state\n-26% throughput}" fillcolor="#00b894" fontcolor=white width=1.0]
|
||
arena [label="{Asm meta-GC arena|peak RSS: 1.2 MB\n2000/2000 resets\nsame throughput as naive}" fillcolor="#6c5ce7" fontcolor=white width=1.0]
|
||
|
||
bump -> gc -> arena
|
||
}
|