Repository navigation
Calls passing a source lambda run a specialized def with the lambda's captures as parameters - #1356
nicolas-abril wants to merge 5 commits into
Conversation
|
Thanks for this. I rewrote my particle sample to fit this change and confirmed performance comparable to the optimized version from #1288. With the original source, this PR reduced the update closure segments from 14 to 12. After adapting the sample, they're down to 0. |
|
Rebased on main 7896791 and pushed three commits on top of the original two. Bugs fixed
Simplifications: the key is built in one pass, specs register themselves (no Not fixed: annotations above the replaced parameter still mention it. I found no case where this matters. Numbers (macOS)
This stays a draft until the cap question is settled: either a cap raise, or about 400 ttok cut elsewhere. Unsupervised AI review: written and checked by an AI agent without line-by-line human review. |
f2fdea6 to
edca1ed
Compare
… captures as parameters (prototype) A call to a def whose argument in a declared live position is a literal lambda, applied there at its own arity, runs c$cbN: the callee's body with the lambda in place of that parameter and the lambda's captures as leading parameters, keyed by the lambda's code (captures abstracted, types dropped) and its parameters' layouts, minted only while the book is read. A substituted lambda carries the span CB, and a lambda that holds one is never a key, so every key is a callee and a source lambda: finite. anf beta-reduces the lambda's applications, binding each argument. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…variable for its position; only a lambda whose free variables are all its live captures gets a spec Two bugs. The key dropped types, so one lambda's code over captures of different layouts shared a def typed by the first caller: C read a String capture at a U32's layout and printed "". And a free variable the lambda did not capture (an erased type argument) went into the key as its probe, which each walk of a spec renews, so a generic callee whose recursion passes such a lambda minted a def per walk and the build never ended. The key now keeps types, every free variable becomes its position, and a spec takes only a lambda whose free variables are live, typed captures: its def is closed, and a key read again under emission (erased parameters instantiated) finds the def the book walk made. A minted spec joins the sources itself (src_add), so file_book needs no hook; anf's beta loses an unreachable branch. tests/run/known_callback_layouts.bend and known_callback_edges.bend fail on the old key (wrong C output; a build that does not end).
… of the book's defs, not the caller's
…pan; a spec's spine carries its new def's tld
edca1ed to
4776ffa
Compare
|
Superseded by #1866, which gets the same speed by inlining instead of specializing defs, at +889 ttok instead of +1,136. |
…e, through a leading one-arm match A call whose argument in a live position is a source lambda inlines a non-recursive callee at the call site, so the lambda is applied in place and no closure is built. Inlining passes through a callee's leading match on a one-constructor type (tuples, records): def_at puts the callback into the match arm. A lambda applied directly to its arguments is reduced in anf. In the emitter, a let bound to a one-arm match moves the rest of the code into that arm, and a tail one-arm match is emitted directly. Skipped: multi-arm matches, recursive callees, callees with more than one non-callback argument left at the match. Replaces bendlang#1356. Fixtures from bendlang#1288 (by @mizchi) included unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Draft:
comp.tsis 64,306 ttok, 306 over the 64,000 cap. Opened to decide whether this is worth the tokens.Follows #1288, which made calls with a known callback skip the closure by inlining the callee at each call site. That needed a fuel budget and couldn't handle recursive callees, so it was closed. This does the same thing in
comp.tsonly, with no fuel and no language change. The JS lane is unchanged.What it does
A call that passes a lambda written in the source, in a declared live parameter position, runs a specialized def
callee$cbNinstead of building a closure:anfreduces the callback's applications, binding each argument.Termination, by construction. A substituted lambda carries a marker span (
CB). A lambda that contains one is never used as a key, except the substituted lambda itself passed on unchanged. So every key is a pair of a callee and a lambda written in the source, and both sets are finite. A callback that a recursion wraps, as ingo(n - 1, y => f(g(y))), keeps its closure.Results
The fixtures from #1288 (by @mizchi) are included unchanged. Each prints
42in the interpreter, JS and native (1 and default threads), and builds no closures beyond the runtime's baseline (main builds 9 and 2).known_callback_shapes.bendcovers:ArrayandListcapturesSpeed (M4, 10⁸ steps, median of 7, run alone; the same at 1 and default threads):
Regression (1,644 programs in
tests/,demos/andbench/, main vs this):#|lines, and all 7 demos build.Compile time (median of 3):
app_slash_boss_3d1.13 → 1.15 s, netcodewalkers_test0.32 → 0.35 s; others unchanged.Not covered
IO.bindapplies its callback to more arguments than the lambda has, which would need let-floating.Maybe.bind,Result.bindandString.wordare covered.Cost and how it could fit the cap
+1,174 ttok:
cb_spine: 757 in total (rejection checks 122, key and captures 278, building the def 275, redirecting the call 82)def_inst, which instantiates the callee's body and type: 203anfbeta reduction: 146Filefields: ~130A review pass on
cb_spinefound only about 6 safe tokens to cut. Getting under the cap would need one of these:def_inst'sMatcase). This losesString.word.go-style matchers that take a continuation.comp.ts.Open risks
term_highercopies probe variables. A collision cancels the specialization; it doesn't miscompile.Not run: the cluster gates and CUDA.
🤖 Generated with Claude Code