bench: mono-vs-vdisp micro-benchmark + revised Decision 11 rationale
Hypothesis-driven measurement of "did monomorphisation actually buy us performance?" on a 100M-iter LCG hot loop, AILang mono'd code vs. four C reference variants (direct-inlinable, direct- noinline, indirect-monomorphic, indirect-polymorphic). Zen 3, clang -O2, median-of-15. Headline: H1 supported, but the mechanism is inlining, not dispatch shape. AILang mono = hand-C direct (1.000x). Indirect- monomorphic = direct-noinline (1.000x) — saturating branch predictor makes the indirect-call cost vanish on this hardware. Inlining is the actual 3.31x win; polymorphic indirect adds another 21% predictor-miss penalty. DESIGN.md Decision 11 gains a rationale paragraph reframing mono as inlining-enabler rather than indirect-call-eliminator, with explicit pointer to the bench. JOURNAL entry records the full methodology, ratios, limitations, and the side-effect mono-pass env.globals-seeding bug surfaced while building the AILang fixture (separate RED-first debug iter to follow).
This commit is contained in:
@@ -1606,6 +1606,23 @@ After this pass, the IR contains no class machinery — only ordinary
|
||||
monomorphic functions and direct calls. Codegen sees no difference
|
||||
between a hand-written `show_int` and a synthesised `show__Int`.
|
||||
|
||||
**Why mono, not virtual dispatch (the empirically-grounded version).**
|
||||
The original rationale implicitly argued that mono saves a per-call
|
||||
indirect-jump cost. The 2026-05-10 mono-vs-vdisp micro-benchmark
|
||||
(`bench/mono_dispatch.py`, JOURNAL entry of the same date) refutes
|
||||
that specific claim: on a saturating branch predictor with a
|
||||
monomorphic indirect target, indirect dispatch is free relative to
|
||||
the same-shape non-inlined direct call (1.000x on Zen 3 / clang -O2).
|
||||
The correct rationale is one level up: **mono makes the call target
|
||||
visible to the optimiser, unlocking inlining and downstream loop
|
||||
transformations that virtual dispatch prevents in principle.** The
|
||||
measured end-to-end win on a tight LCG hot loop is 3.31x vs
|
||||
non-inlinable direct call and 4.00x vs polymorphic vdisp (4 distinct
|
||||
targets cycling). On larger callee bodies or cold call sites the
|
||||
inlining win shrinks toward zero, but the architectural claim — "mono
|
||||
enables optimisations vdisp forbids" — holds across the spectrum,
|
||||
while the older "saves an indirect call" framing does not.
|
||||
|
||||
The separator is `__` rather than `#` or `@` because `#` and `@`
|
||||
are invalid in LLVM IR global identifiers (the IR verifier rejects
|
||||
them inside `@ail_<module>_<def>` mangled names). `__` is legal in
|
||||
|
||||
Reference in New Issue
Block a user