Speed up the interpreted matcher: VarRef byte re-check and a fused child-mask walk - #127
Closed
MesTTo wants to merge 3 commits into
Closed
Speed up the interpreted matcher: VarRef byte re-check and a fused child-mask walk#127MesTTo wants to merge 3 commits into
MesTTo wants to merge 3 commits into
Conversation
added 2 commits
July 2, 2026 22:04
The interpreted matcher re-checked a VarRef (a repeated pattern variable) by pushing the data subterm bound at the first occurrence and re-matching it structurally, which args-decomposes the data subterm, the dominant matcher cost. When the bound value is ground, re-check it by exact byte descent instead (WAM unify_value): descend the bound bytes, and the moment the data branches on a variable child, fall back to the recursive re-match. Correctness: - kernel/resources/formal/verus/VarRefRecheck.rs proves (verus, 5 verified, 0 errors) the gate: for ground g and ground data the byte comparison equals the structural match (T1), byte equality is always sound (T2), and a non-ground data position can match without byte-equality (T3), so the data, not just the bound value, must be ground. The implementation meets T3 by detecting a data variable branch and falling back. - A differential oracle test (varref_fast_recheck_matches_recursive_path) runs ten coreference programs both ways, including the Verus T3 non-ground witness, and asserts identical result sets. The fast path drops duplicate re-matches, so counts may differ; the sets must not. - Every kernel/resources program at several step depths, Nil's bfc chainer at proof size 19, and a 4096-hub triangle produce identical result spaces against the pre-change binary. Measured (min-of-3, interleaved): bench process_calculus 31,526,693 -> 26,369,520 us, 16.4% faster. Gated behind a thread-local toggle the differential test flips.
The NewVar arm of coreferential_transition read each data node's child mask in three masked passes (and(VARS), and(SIZES), and(ARITIES)). Fuse them into a single walk over the mask words, preserving the exact VARS -> SIZES -> ARITIES visit order (VarRefs ascending from their word, NewVar from the high word's bit 0, SymbolSizes from its bits 1..63, Arities from word 0), so the produced match stream stays byte-identical, order included. The size or arity is the bit index itself, dropping the three ByteMask::and ops, the three iterator drains, and the per-child byte_item re-decode; each set child is still descended once. A differential oracle (match_any_fused_equals_three_pass) runs the fused walk against the three-pass on data mixing every child tag class at one node, variables, coreference, several symbol sizes and arities, and deep nesting, and asserts an identical result list AND order; the three-pass is retained behind a cfg(test) toggle to drive it. Every kernel/resources program at several step depths, Nil's bfc chainer at proof size 19, and a 4096-hub triangle stay byte-identical against the pre-change binary. Measured together with the previous commit (min-of-3, interleaved): bench process_calculus 31,461,091 -> 21,248,837 us, 32.5% faster than the pre-change binary.
Collaborator
|
Hi! Appreciate your work here. The MORK kernel is closed to constant-time speed-ups until the asymptotic runtime has no known deficiencies. This prevents us from locking out much more impactful changes. Thanks for your understanding. |
… copy buffer The full bench suite caught what the targeted runs missed: the VarRef fast path made tile_puzzle_states 40x slower. It ran the data-variable branches before pushing the bound value, so every variable child enumerated candidates with the wrong continuation; query_multi's whole-tuple unify rejected them all, results stayed byte-identical, and the walk cost exploded, since the puzzle re-checks boards against move and empty facts whose columns are almost all variables. The fast path now takes over only when the start node has no variable children (the vs! it skips is then a no-op, so the original ordering is preserved byte for byte wherever variable children exist), descends subterm-aware, reading the child mask once per subterm rather than per byte and bulk-checking symbol payloads, and a variable branch below the start falls back to the structural re-match. The re-check's copy of the bound value also reuses a thread-local buffer, the remaining gap on ground-heavy workloads with tiny bound values. Measured (min over interleaved reps, against the pre-series binary): tile_puzzle_states 7.40s -> 7.10s (300s+ before this commit); process_calculus 31.4 -> 21.1s; clique 15.5 -> 15.3s; transitive 31.9 -> 31.7s; odd_even_sort 7.0 -> 6.8s; counter_machine 1.3 -> 1.0s; bfc 14.5 -> 13.1s. Resources, bfc at proof size 19, the triangle, and a distilled tile-puzzle program stay byte-identical, and the oracle gains the schematic-boards shape.
Contributor
Author
|
Closing per the same guidance: the kernel is closed to constant-time speed-ups until the asymptotic runtime has no known deficiencies. The VarRef byte re-check / fused child-mask walk is a constant-factor matcher optimization, so parking it. |
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.
Two fast paths in
coreferential_transition, the interpreted matcher, byte-identical byconstruction and by oracle, together 32.5% on
bench process_calculus.Re-check repeated variables by byte comparison (WAM
unify_value). The matcher re-checked aVarRef, a repeated pattern variable, by pushing the data subterm bound at the first occurrence and
re-matching it structurally, which
args-decomposes the data subterm on every re-check, thedominant matcher cost. When the bound value is ground, the re-check is now an exact byte descent:
walk the bound bytes down the trie, and the moment the data branches on a variable child, fall
back to the recursive re-match for the whole subterm. The gate is proved in
kernel/resources/formal/verus/VarRefRecheck.rs(verus, 5 verified, 0 errors): on ground data thebyte comparison equals the structural match, byte equality is always sound, and a non-ground data
position can match without byte equality, which is why the fallback keys on the data, not just the
bound value. A differential oracle (
varref_fast_recheck_matches_recursive_path) runs tencoreference programs with the fast path on and off, the non-ground witness included, and asserts
identical result sets (the fast path drops duplicate re-matches, so match counts may differ; the
sets must not).
Fuse the three child-mask passes into one word-walk. The NewVar arm read each data node's
child mask three times (
and(VARS),and(SIZES),and(ARITIES)) with an iterator drain and abyte_itemre-decode per child. The fused walk reads each mask word once, in the exactVARS -> SIZES -> ARITIES visit order (VarRefs ascending, NewVar, symbol sizes ascending, arities
ascending), taking the size or arity from the bit index itself. A second oracle
(
match_any_fused_equals_three_pass) runs the fused walk against the original three-pass, whichis retained behind a
cfg(test)toggle, on data mixing every child tag class at one node, andasserts an identical result list and order.
Measured, min of 3 interleaved against the pre-change binary on one machine:
bench process_calculus31,526,693 -> 26,369,520 us, 16.4% fasterbench process_calculus31,461,091 -> 21,248,837 us, 32.5% fasterByte-identical along the way: every
kernel/resourcesprogram at several step depths, Nil's bfcchainer at proof size 19 (801 steps, a 2.9M-atom space), and a 4096-hub triangle produce identical
result spaces against the pre-change binary.
The two commits stack in one PR because both rewrite arms of
coreferential_transition; separatePRs would conflict. Independent of #124 and #126 (#126 touches only
expr; if this lands with it,the wins compose).