Update whitepaper: MOAD audit, before/after, shared heart
Section 12: MOAD Audit — Fixing What We Built
12.1: MOAD-0001 in our own intern_symbol (before/after asm code,
2.9x speedup, O(N) → O(1) hash table)
12.2: MOAD-0002 intertangle in all three implementations
(documented intentional coupling in globals)
12.3: Our Shared Infrastructure — fixing our own sediment teaches
us to recognize it in others. A hash table is not an
optimization, it is the removal of unnecessary suffering
from a system that deserves better.
"Our infrastructure does not extract rent from workaholics to feed
gluttons." — reducing stress on our shared computational heart.
This commit is contained in:
parent
22571fa470
commit
7ffd01f656
2 changed files with 607 additions and 237 deletions
File diff suppressed because one or more lines are too long
|
|
@ -587,7 +587,66 @@ Assembly times include process startup and parsing.
|
|||
All pass via ``make test-all``.
|
||||
|
||||
|
||||
12. Future Work
|
||||
12. MOAD Audit: Fixing What We Built
|
||||
--------------------------------------
|
||||
|
||||
We scanned all three implementations for the five MOADs. Every project contains its own sediment.
|
||||
|
||||
12.1 MOAD-0001: The Sedimentary Defect in Our Own Code
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The assembly interpreter's ``intern_symbol`` used a linear scan through all interned symbols — O(N) per lookup, O(N²) over a program's lifetime. For a program defining 34 builtins plus user symbols, every ``define``, every lambda parameter, every variable reference walked the entire table.
|
||||
|
||||
**Before (linear scan)**::
|
||||
|
||||
.isym_search:
|
||||
cmpq %rcx, %r8 # compare lengths
|
||||
jne .isym_next
|
||||
rep cmpsb # compare bytes
|
||||
je .isym_found
|
||||
.isym_next:
|
||||
addq $24, %rax # next entry
|
||||
jmp .isym_search # O(N) per intern
|
||||
|
||||
**After (djb2 hash table, 1024 buckets)**::
|
||||
|
||||
.intern_hash:
|
||||
imulq $33, %rax # djb2: hash = hash * 33 + c
|
||||
addq %rcx, %rax
|
||||
loop .intern_hash
|
||||
andq $1023, %rax # bucket = hash & (1024-1)
|
||||
# O(1) average lookup
|
||||
|
||||
The fix: 99 lines changed, 1024-bucket hash table with chaining. **2.9x faster** on a 2000-symbol stress test. On benchmarks with fewer symbols (``ack``, ``fib``), the improvement is modest (15% on ``sum-to(50k)``), because the linear scan was already fast at small N. The fix pays off at scale — the same pattern as MOAD-0001 everywhere: invisible at small inputs, catastrophic at large ones.
|
||||
|
||||
The Python implementation had a similar defect: ``_define_record_type`` used ``list.index()`` for field lookup. Replaced with a dict. O(N) → O(1).
|
||||
|
||||
12.2 MOAD-0002: The Intertangle in Our Own Design
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All three implementations share mutable global state between subsystems:
|
||||
|
||||
- **Assembly**: The global environment lives in register ``%r14``. Every ``define``, ``set!``, and ``eval`` mutates it directly. This works because the assembly interpreter is single-threaded & sequential, but it means the evaluator, the environment manager, & the builtin system are inseparable. You cannot test one without the others.
|
||||
|
||||
- **C**: Thread-local ``g_error_ctx``, ``cc_escape_val``, & ``cc_active_jmp`` couple error handling & call/cc across all modules. These are necessary for ``setjmp``/``longjmp`` but they mean the JIT, the evaluator, & the continuation system cannot be reasoned about independently.
|
||||
|
||||
- **Python**: ``_portal_checkpoint``, ``_call_stack``, ``_modules``, ``_record_types``, ``_auto_compile`` — five module-level mutable globals that different subsystems read & write. The portal system, the error reporter, the module loader, & the compilation strategy are all coupled through shared state.
|
||||
|
||||
We documented these rather than refactoring them. In each case, the coupling exists for performance (the globals are on hot paths) or necessity (``setjmp`` requires thread-local state). The documentation makes the coupling visible so future work can decouple selectively.
|
||||
|
||||
12.3 Our Shared Infrastructure
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
*"A diagram is worth 10,000 words."* — russell@unturf.com
|
||||
|
||||
Every MOAD we fixed in our own code is a MOAD we understand better when we find it in others. The sedimentary defect in ``intern_symbol`` is the same pattern as the sedimentary defect in Lean 4's ``check_duplicated_univ_params``. The intertangle in our global environment register is the same pattern as the intertangle in any system that routes state through implicit globals instead of explicit parameters.
|
||||
|
||||
**Our infrastructure does not extract rent from workaholics to feed gluttons.** A symbol table that does O(N) work per lookup is a workaholic node — it does more work than necessary on every operation, and that cost compounds through every downstream consumer. Fixing it reduces stress on our shared computational heart. The CPU cycles saved are cycles available for the next lambda, the next continuation, the next portal resume.
|
||||
|
||||
This is the permacomputer obligation: infrastructure that renews itself. Code that gets faster as we understand it better. A hash table is not an optimization — it is the removal of unnecessary suffering from a system that deserves better.
|
||||
|
||||
|
||||
13. Future Work
|
||||
----------------
|
||||
|
||||
- **GPU lambda execution**: Map/reduce on CUDA for data-parallel Scheme (Phase 1), trampolining for recursive lambdas (Phase 2), interaction combinators for massive parallelism (Phase 3)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue