Repository navigation
Conversation
|
Thanks for this. The per-commit write-up made it easy to check, and the numbers hold up. A few things before it can go in:
What I checked, with the PR merged onto main: test 1639/1639, ping 59/59, perf 124/124 (on the minis, vs main: SEQ-CPU geomean 0.972, PAR-CPU 0.982; tree-bitonic 0.85, symreg 0.89, tree-matmul 0.92), and safe tests with 0 false rejects. 800 programs run natively at --threads 1/8/default print the same as on main, except brw_fork_push. Heap-checked and ASan/UBSan builds are clean. The JS output is identical apart from the cut comments. The lookup repro holds: find goes from 96 to 31 ms on an M4 Pro. |
b0b6b2f to
82130a1
Compare
|
Thanks, all three are done, and the branch is rebuilt on main at 234cb9b:
Main is at 64,092 by itself, so a fifth commit cuts comments to bring |
Compacts scope_new, folds call_eta's early return, and joins wrapped lines that fit in 80 columns. C and JS output is byte-identical for every bench and runnable test.
When each arm of an if/else chain allocates one node of the same class outside any inner block, allocate it once before the chain. Clang then turns the arms' stores into selects instead of a branch on the tag.
A host continuation frame now stores its segment's address (WL_K), so WL_RETN tail-calls it directly instead of loading it from wl_tab first. Device frames keep fids. WL_JMP still compiles to a direct branch.
A def that only reads a borrowed parameter, but passes one field of it to an owned position (a lookup returning its match's value), owned the whole parameter: every node it walked went through ctr_take, which on a shared value bumps each field, releases the node and sinks the rest. val_own now keeps such a field where it escapes (term_keep) and leaves the parameter borrowed, so its nodes are read with term_peek. The root itself is never kept. A kept field must already be sealed, so its root's type heats (heat), but only once a pass changes no fact: the first pass borrows every parameter, and heating there left types hot for roots that later become owned (merkle's Mt). The root's def comes from its key, not the scope, as a fused callee's body runs under the callee's def. A walk over a shared 20000-entry List<&2, Entry> that returns its match's value, missing 500 times at --threads 1, goes from 147 ms to 46 ms, about the time of the same walk returning Bool (44 ms). The C of 12 test programs and the hvm5_mini demo changes, and of no bench; JS is unchanged. New witnesses: tests/reg/brw_field_keep.bend and brw_field_fuse.bend (heating the fused callee's parameters printed freed memory). The comments of brw_field_store, brw_fork_cross and brw_mixed_args follow the new conventions; brw_twins hands its twin to Xs.push, which still owns its parameter, so it still covers one borrowed and one owned use. comp.ts: 66,120 -> 66,223 tokens; the next commit brings it under the cap. Comments are cut here too: the Scope comment no longer lists the fields its type declares, the File and val_own comments share less, and compile_book drops its note that a pure main's descriptor roots the datatypes it prints (the Show section describes the descriptor, and the next lines root them).
On main, commits 1 to 4 take comp.ts from 65,916 to 66,223 tokens; this
commit brings it to 65,811 / 66,000. Two code edits keep their output:
emit_native hands emit_body its own f (fun_at's copy holds the same h),
and sizes a spin with body.join("\n") instead of a reduce. The rest are
comments that restate code a few lines away, or a twin elsewhere in the
file:
- the header's note that the runtimes close the file (the section
headers show it);
- the constants' SPIN_FAR sentence (the C runtime's long-spin comment
says it in full);
- "the Array operations have no C text" (array_clone and the atomics
have some; arr_op's call shows the rest);
- the Optimized comment's gloss of Native's fields (its type);
- char_new's encoding (its body) and that the JS runtime holds its text
(the line that splices it);
- the Term comment's literal sentence (lit_call, below it);
- ty_holds' lead clause (its signature);
- the Lay comment's parenthetical on an element's open layout (lay_el);
- lay_at's comment (its one-line body);
- the Mat comment's U32 table rule (mat_rows' condition);
- the normal-form comment's erased-application clause (anf's Var case);
- the Emit comment's "a read asks a lend" (the File comment);
- emit_open's note on a foreign def's continuation (the Emit comment
and fun_of's lays);
- "an effect source is read once, in one namespace" (effect_srcs' map
and its error);
- the JS comment's closure, $pc and direct-call clauses (js_lib and
js_def);
- host's U+FFFD clause (the Char comment);
- the Dialect comment's segment-dispatch sentence (the WL macros below);
- the Globals comment's device-program source (BEND_SRC);
- the Gpu comment's "a launch loads it, else notes and compiles" (both
gpu_load bodies);
- the Corpus comment's GPU sentence (corpus_grow, corpus_setup);
- the Io comment's request and parked-field sentences (io_step,
io_wait_on);
- io_loop's busy-loop comment (the (n & 63) block);
- the Show comment's show_f32 sentence (f32_text's round trip);
- the JS Io comment's deadline-order sentence (io_park_on's sorted
insert, and the C io_park comment);
- io_fail's errno fallback (its first line).
C and JS output lose only those comment lines.
82130a1 to
e603584
Compare
|
fixed conflicts @Lorenzobattistela |
|
Thanks for the work on this. We're closing it in favour of a smaller PR. Please open a new one with just the allocation hoist (commit 2), rebased on current main. It's the one change here that clearly pays off, and it's small. What we measured, with the PR merged onto main b0d1ca6 (the 17 conflict hunks with #1786 are comment-only and were resolved to main's side):
SEQ-CPU on the minis, main → PR:
The other benches are within 1%. PAR-CPU follows the same pattern: tree-bitonic -10%, symreg -8%, editdist +16%. Commit 3 (address frames) is the editdist regression. With commits 1-4 cherry-picked onto main one at a time and built on an M4 Pro, commit 3 alone takes editdist from 2.05 s to 2.47 s. With -falign-functions=64, both builds run at 2.08 s, so it is layout, but on M4 it goes the other way from your M3 Max, and the gate grades the default build. Its other gains are a few percent (kmeans), and host and device frames would hold different things for good, so we'd rather not take it. Commit 4 (the borrowed-field keep) changes no bench and breaks three programs that run on main, all at --threads 1:
It also adds a full extra emit pass for the heat step, and a seal set that every copy of brwl has to remember (val_arms forgets it). If you want to come back to it, it should be its own PR, with those three fixed and tests for Array fields, closure fields and captures. For the hoist PR:
|
What changes
Rebased on main at 881cb2e. After review, the refcount commit is gone, and the fork frame write-back went in on its own as #1615.
file_newkeeps its typed literal andemit_foldkeeps its early returns. All five commits are inbend2/comp.ts; the fourth also adds or updates borrow witnesses undertests/reg.scope_new, foldscall_eta's early return, and joins wrapped lines that fit in 80 columns. C and JS output is byte-identical to main for all 840 programs that build (tests, benches and demos).if/elsechain allocates one node of the same size class outside any inner block,emit_chainallocates it once before the chain. Before, the inlineheap_allocfast/slow branch in each arm kept clang from merging the arms. Now their stores become selects instead of a branch on the tag. The C of 41 programs changes, 3 of them benches (tree-bitonic, tree-radix and symreg).WL_K), soWL_RETNtail-callsSTK(0)directly instead of loading it fromwl_tabfirst. Device frames keep fids.WL_JMPstill compiles to a direct branch.ctr_take, which on a shared value bumps each field, releases the node and sinks the rest. Nowval_ownkeeps that field where it escapes (term_keep), and the parameter stays borrowed, so its nodes are read withterm_peek. The root itself is never kept.FL.heat), but only once a pass changes no fact. The first pass borrows every parameter, and heating there left types hot for roots that later become owned (merkle'sMt).tests/reg/brw_field_fuse.bendprinted freed memory when that callee's parameter types were heated instead.pure_hvm5_minidemo changes, and of no bench.comp.ts tokens
Main is at 65,916 / 66,000 by itself (#1626 raised the cap to 66,000). Commits 1 to 4 take it to 66,223, and commit 5 brings it to 65,811. On the rebase, one cut met a new sentence from #1653 in the same comment: the cut is kept and the new sentence stays.
Commit 5 makes two code edits, both with the same output.
emit_nativehandsemit_bodyits ownf, becausefun_at's copy holds the sameh. It also sizes a spin withbody.join("\n")instead of a reduce. Everything else in it is a comment cut, each one restating code a few lines away or a twin elsewhere in the file:SPIN_FARsentence (the C runtime's long-spin comment says it in full);array_cloneand the atomics have some;arr_op's call shows the rest);Native's fields (its type);char_new's encoding (its body) and the note that the JS runtime holds its text (the line that splices it in);lit_call, below it);ty_holds' lead clause (its signature);lay_el);lay_at's comment (its one-line body);mat_rows' condition);anf'sVarcase);emit_open's note on a foreign def's continuation (the Emit comment andfun_of's lays);effect_srcs' map and its error);$pcand direct-call clauses (js_libandjs_def);host's U+FFFD clause (the Char comment).WL_macros below it);BEND_SRC);gpu_loadbodies);corpus_growandcorpus_setup);io_stepandio_wait_on);io_loop's busy-loop comment (the(n & 63)block);show_f32sentence (f32_text's round trip).io_park_on's sorted insert, and the Cio_parkcomment);io_fail's errno fallback (its first line).Commit 4 also rewords the File, Scope and val_own comments to share less. It drops
compile_book's note that a pure main's descriptor roots the datatypes it prints: the Show section describes the descriptor, and the next lines root them.Checks
bun gates/repo.ts: 54 / 54.tsc -p bend2/pack/tsconfig.json(TypeScript 5.9.3): the same 11 errors as main, none incomp.ts. The TS2352 fromfile_new's cast is gone.--threads 1,8and16, andpure_hvm5_miniprints the same under both builds.gates/test.ts, run locally: PASS 1675 / 1677. The two failures,gfx_clicksandgfx_window, expect "no display" and fail the same way on main here, because this Mac has a desktop session.Lookup repro (commit 4)
The program below walks a shared 20000-entry
List<&2, Entry>500 times, missing every time.findreturns the match's value andhasreturns a Bool. It was built withclang -O3, each binary warmed once, then run 3 interleaved rounds with./walk --threads 1 -- 20000 500on an Apple M3 Max:main.bend
Benches
Measured on the previous base, main at 234cb9b. The rebase left the five commits' diffs unchanged but for the one resolved comment. Apple M3 Max,
--gpu off. Each binary runs once to warm up, then the builds alternate for 3 rounds (SEQ,--threads 1) or 5 rounds (PAR,--threads 16). The tables show the minimum, and each ratio is to main (lower is faster). "PR, aligned" compares main and this branch with both built with-falign-functions=64. That column takes function layout out of the comparison, since layout alone moves some of these benches by a few percent. The PR binaries are built from commit 4; commit 5 changes only comments in the C.SEQ
PAR
Notes: