Repository navigation
runner: marshal strings 7.7× faster (5 MB: 1534 → 200 ms) - #38
Merged
Merged
Conversation
`bind` of the 5 MB forest source (3.16M code points) took 1.06 s, all of it
in of_string: of_nat rebuilt and re-interned every character's bit list (a
dozen hash-consing probes per character, all hits), and the list cells went
through fork() one at a time, growing the table by doubling as they came.
- Each code point's nat is built once and shared (a file has a few dozen
distinct ones); what is left per character is its list cell.
- The cells are built by a new list() on the evaluator. Built from the end,
a cell is only searched for until one comes out new: a node that already
existed cannot have a child that did not, so every cell in front of it is
new too and just takes a slot, in a table sized once for all of them. The
lazy evaluator, which does not hash-cons, gets the plain loop.
- The exactness of hash-consing, which this and the memo rely on and a
native fast path deciding equality by index would too, is now stated
where the table is declared.
bind ~input, 5 MB file, runner-stats, min of 5 (warm memory):
base 1055 ms
code-point cache, fork() per cell 340 ms
+ table presized, full search 125 ms
+ list(): no search once cells are new 103 ms <- this commit
bound for not inserting new cells at all 56 ms (list() minus its table
writes: not exact, measured only to price the alternative)
The remaining ~50 ms of table writes are one cache miss per cell; saving
them would take intern() consulting a second, structural index of such
runs on every miss, kept exact across collections — not worth 50 ms/5 MB.
1.26 MB file: 204 ms -> 33 ms; compiler.lamb (66 KB): 16 ms -> 3 ms.
Output (`reduce string` and `reduce dag` of the bound string, incl. 2-,
3- and 4-byte UTF-8, a string sharing a suffix with an earlier one, and one
bound twice) is byte-identical to the base runner; compile_file output on
all three benchmark sources unchanged; test.sh passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ffv5AybPTpV56D5vuayQr
to_string_marshal decoded each character by materializing its bit list
into a std::vector (to_list) and walking that backwards: an allocation
and a free per character of every string answer, which a compile session
answers a lot of (compiler.lamb alone compiles to 174 KB of text).
perf put malloc/free and the vector growth at ~4% of the replayed compile
session. The bits are now read off the list in place, LSB first, with the
same results and the same errors for a list or bool that is malformed on
its own (a tree malformed both ways may now report its first defect
rather than the spine's).
compile session of the arboretum build (86 sources), 3 interleaved runs:
3.59-4.27 s -> 3.09-3.57 s
Session output identical; string round-trip (2-4 byte UTF-8) identical;
test.sh passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ffv5AybPTpV56D5vuayQr
This was referenced Sep 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Marshalling a string into a tree (
bind): 7.7× faster. Reduction untouched.simple/paper_experiments)termination/paper_experiments)compiler.lamb(66 KB)list()builds a string's cells in bulk: it searches for a cell only until one comes out new (every cell in front of a new one is new too), with the table sized once.Exact hash-consing is kept, and now stated where it is relied on.
First of a stack, in landing order: #38 marshal → #39 step → #40 proofs → #41 jets → #42 runs. Independent of it: the compiler rewrite, lambada-llc/arboretum#70 + lambada-llc/lambada#41.
Measured with
runner -s(eager), compile_file (lambada main) on the source given, min of 5 interleaved runs on a 4-CPU VM; outputs byte-identical to main.🤖 Generated with Claude Code
https://claude.ai/code/session_018ffv5AybPTpV56D5vuayQr