07bff24527
Two new throughput fixtures targeting blind spots in the 21'a
corpus:
- bench_closure_chain exercises the build_pair_drop_fn codegen
path (the 18c.4 doubled-braces trigger). Each iteration of
run_loop allocates a {thunk, env} closure pair via the
let-rec-name-as-value escape route. Sizes 10k / 100k / 500k.
rc/bump = 4.14x — materially worse than the 2.91x / 2.59x of
the linear/tree fixtures, exposing that closure work pays the
RC alloc tax twice (pair + env-struct).
- bench_hof_pipeline exercises poly-ADT instantiation and
indirect dispatch via fold_with_fn over List<a>. Sizes 100k /
1M / 3M elements. Ratios essentially match bench_list_sum,
confirming the 13b static-template-plus-ctor-inline design
has zero measurable overhead at this scale.
Baseline file extends from 31 to 47 metrics. The two new fixtures
build clean under all three allocators; the rc-arm build exercises
the per-type drop fn for the closure-pair, providing a tripwire
for any future 18c.4-class IR malformedness.
JOURNAL records both surprises (4.14x closure tax, ~zero HOF/poly
overhead) and explicitly notes the dispersion observation on
explicit_at_rc.p99 — three captures today (357.5 / 294.6 / 251.5)
confirm wide run-to-run variance on that fixture. Methodology
upgrade (n>=10 captures or tighter fixture) deferred to 21'c.
bench/run.sh fixtures array updated. bench/check.py needed no
changes — its parser handles the wider table by metric name.
69 lines
2.4 KiB
Plaintext
69 lines
2.4 KiB
Plaintext
; Bench fixture: closure-pair allocation under load.
|
|
;
|
|
; Each iteration of `run_loop` introduces a fresh `let-rec helper`
|
|
; whose body captures the outer fn-param `i`. The let-rec name is
|
|
; passed as a value to `apply_thrice`, which forces eta-Lam wrapping
|
|
; (the 16b.5 / 8b ABI shape) — codegen allocates a `{ thunk, env }`
|
|
; closure pair where `env` holds the captured `i`. Under
|
|
; --alloc=rc the allocation goes through `ailang_rc_alloc` and the
|
|
; per-type drop fn `build_pair_drop_fn` is generated for the
|
|
; closure-pair type. (The 18c.4 doubled-braces bug lived in exactly
|
|
; this drop fn; this fixture is the canonical exerciser.)
|
|
;
|
|
; The helper body is (body i) — it ignores its parameter and
|
|
; returns the capture. apply_thrice(helper, x) therefore evaluates
|
|
; to i regardless of x. Each run_loop iteration thus contributes
|
|
; `i` to the accumulator. Total = sum 0..n-1 = n*(n-1)/2.
|
|
;
|
|
; Sizes (Implicit-mode RC leaks the closure pairs by design — the
|
|
; gc/bump arms are the meaningful allocator comparison, the rc arm
|
|
; reports alloc-tax-only):
|
|
; n = 10_000 -> 49_995_000 (1k closure-pair allocs)
|
|
; n = 100_000 -> 4_999_950_000 (100k allocs)
|
|
; n = 500_000 -> 124_999_750_000 (500k allocs)
|
|
|
|
(module bench_closure_chain
|
|
|
|
(fn apply_thrice
|
|
(doc "Higher-order: apply f three times to seed.")
|
|
(type
|
|
(fn-type
|
|
(params (fn-type (params (con Int)) (ret (con Int)))
|
|
(con Int))
|
|
(ret (con Int))))
|
|
(params f seed)
|
|
(body
|
|
(app f (app f (app f seed)))))
|
|
|
|
(fn run_loop
|
|
(doc "For each i in [n-1, n-2, ..., 0], build a closure capturing i, pass to apply_thrice, accumulate. Tail-recursive on i and acc.")
|
|
(type
|
|
(fn-type
|
|
(params (con Int) (con Int))
|
|
(ret (con Int))))
|
|
(params i acc)
|
|
(body
|
|
(if (app < i 0)
|
|
acc
|
|
(let-rec helper
|
|
(params x)
|
|
(type (fn-type (params (con Int)) (ret (con Int))))
|
|
(body i)
|
|
(in
|
|
(let r (app apply_thrice helper i)
|
|
(tail-app run_loop (app - i 1) (app + acc r))))))))
|
|
|
|
(fn run
|
|
(doc "Drive run_loop from i=n-1 down to 0.")
|
|
(type (fn-type (params (con Int)) (ret (con Int))))
|
|
(params n)
|
|
(body (app run_loop (app - n 1) 0)))
|
|
|
|
(fn main
|
|
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
|
(params)
|
|
(body
|
|
(seq (do io/print_int (app run 10000))
|
|
(seq (do io/print_int (app run 100000))
|
|
(do io/print_int (app run 500000)))))))
|