mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
perf(javascript): index member-effect paths for projected reads (#8108)
* perf(javascript): index member-effect paths for projected reads Each projected read scanned every member-write record of its binding and tested the record path against the selection. On the #7774 reproducer only 3.7% of scanned records pass that test. Index each binding's append-only records by path in a lazily built trie that extends itself with newly appended records, and visit only records whose exact keys a selected choice names or whose keys are wildcards. Candidates come back in record order and still pass the exact predicate, now owned next to the index as semanticEffectPathMatches. The semantic IR and the complete analyze-javascript-application Evidence are byte-identical. On the full reproducer each worker phase drops from about 80 s to 29 s, and to 8 s together with #8103. Refs #7774 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(javascript): stop escape checks at the first indexed match The escape check only asks whether any matching escape is unsuperseded, so it does not need ordered candidates. Share one trie traversal between the ordered write candidates and an early-exit visitor for this check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6bb831df60
commit
767f6afc18
3 files changed
+268
-20
No files matched your search
@@ -0,0 +1,140 @@
|
||||
import type { JavaScriptSemanticMemberEffect } from "./javascriptSemanticState.js";
|
||||
import {
|
||||
semanticAliasSelectionChoices,
|
||||
type SemanticAliasSelectionPath,
|
||||
} from "./javascriptSemanticAliasSelections.js";
|
||||
import {
|
||||
semanticPropertyPathKeyMatches,
|
||||
type JavaScriptSemanticPropertyPath,
|
||||
} from "./javascriptSemanticPropertyPaths.js";
|
||||
|
||||
/** Whether an effect recorded at one member path can reach a projected read. */
|
||||
export const semanticEffectPathMatches = (
|
||||
effectPath: JavaScriptSemanticPropertyPath,
|
||||
path: SemanticAliasSelectionPath,
|
||||
): boolean =>
|
||||
path.length > 0 &&
|
||||
effectPath.length <= path.length &&
|
||||
effectPath.every((key, index) => {
|
||||
const selected = path[index];
|
||||
return (
|
||||
selected !== undefined &&
|
||||
semanticAliasSelectionChoices(selected).some(
|
||||
(choice) =>
|
||||
choice === null ||
|
||||
typeof choice === "object" ||
|
||||
semanticPropertyPathKeyMatches(key, String(choice)),
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Most nodes are leaves of one path. Materialize each collection only when it
|
||||
// holds a record or child.
|
||||
interface EffectPathNode {
|
||||
/** Record positions whose recorded path ends at this depth. */
|
||||
ends?: number[];
|
||||
/** Children for exact keys, by their string property name. */
|
||||
exact?: Map<string, EffectPathNode>;
|
||||
/** Child for a wildcard or copied-slot key, which can match any name. */
|
||||
open?: EffectPathNode;
|
||||
}
|
||||
|
||||
interface EffectPathIndex {
|
||||
readonly root: EffectPathNode;
|
||||
indexed: number;
|
||||
}
|
||||
|
||||
// Binding member effects are append-only, so each index extends itself with
|
||||
// records appended since its previous query and lives as long as its records.
|
||||
const effectPathIndexes = new WeakMap<
|
||||
readonly JavaScriptSemanticMemberEffect[],
|
||||
EffectPathIndex
|
||||
>();
|
||||
|
||||
const currentEffectPathIndex = (
|
||||
effects: readonly JavaScriptSemanticMemberEffect[],
|
||||
): EffectPathIndex => {
|
||||
let index = effectPathIndexes.get(effects);
|
||||
if (index === undefined) {
|
||||
index = { root: {}, indexed: 0 };
|
||||
effectPathIndexes.set(effects, index);
|
||||
}
|
||||
for (; index.indexed < effects.length; index.indexed++) {
|
||||
let node = index.root;
|
||||
for (const key of effects[index.indexed]!.path) {
|
||||
if (key === null || typeof key === "object") node = node.open ??= {};
|
||||
else {
|
||||
const exact = (node.exact ??= new Map());
|
||||
const name = String(key);
|
||||
let child = exact.get(name);
|
||||
if (child === undefined) {
|
||||
child = {};
|
||||
exact.set(name, child);
|
||||
}
|
||||
node = child;
|
||||
}
|
||||
}
|
||||
(node.ends ??= []).push(index.indexed);
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
// Visit record positions whose recorded path may match a projected read, in
|
||||
// index order. Only records longer than the read, or with an exact key that no
|
||||
// selected choice names, are skipped; wildcard and copied-slot keys remain.
|
||||
// Returns true as soon as `visit` does.
|
||||
const visitEffectPathCandidates = (
|
||||
effects: readonly JavaScriptSemanticMemberEffect[],
|
||||
path: SemanticAliasSelectionPath,
|
||||
visit: (position: number) => boolean,
|
||||
): boolean => {
|
||||
const pending = [{ node: currentEffectPathIndex(effects).root, depth: 0 }];
|
||||
while (pending.length > 0) {
|
||||
const { node, depth } = pending.pop()!;
|
||||
for (const position of node.ends ?? []) if (visit(position)) return true;
|
||||
if (depth >= path.length) continue;
|
||||
if (node.open !== undefined)
|
||||
pending.push({ node: node.open, depth: depth + 1 });
|
||||
const exact = node.exact;
|
||||
if (exact === undefined) continue;
|
||||
const choices = semanticAliasSelectionChoices(path[depth]!);
|
||||
if (choices.some((choice) => choice === null || typeof choice === "object"))
|
||||
for (const child of exact.values())
|
||||
pending.push({ node: child, depth: depth + 1 });
|
||||
else
|
||||
for (const name of new Set(choices.map((choice) => String(choice)))) {
|
||||
const child = exact.get(name);
|
||||
if (child !== undefined)
|
||||
pending.push({ node: child, depth: depth + 1 });
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Effects whose recorded path may match a projected read, in record order.
|
||||
* Candidates are a superset, so callers still apply `semanticEffectPathMatches`.
|
||||
*/
|
||||
export const semanticEffectPathCandidates = (
|
||||
effects: readonly JavaScriptSemanticMemberEffect[],
|
||||
path: SemanticAliasSelectionPath,
|
||||
): readonly JavaScriptSemanticMemberEffect[] => {
|
||||
const positions: number[] = [];
|
||||
visitEffectPathCandidates(effects, path, (position) => {
|
||||
positions.push(position);
|
||||
return false;
|
||||
});
|
||||
return positions
|
||||
.sort((left, right) => left - right)
|
||||
.map((position) => effects[position]!);
|
||||
};
|
||||
|
||||
/** Whether any candidate effect for a projected read satisfies `predicate`. */
|
||||
export const semanticSomeEffectPathCandidate = (
|
||||
effects: readonly JavaScriptSemanticMemberEffect[],
|
||||
path: SemanticAliasSelectionPath,
|
||||
predicate: (effect: JavaScriptSemanticMemberEffect) => boolean,
|
||||
): boolean =>
|
||||
visitEffectPathCandidates(effects, path, (position) =>
|
||||
predicate(effects[position]!),
|
||||
);
|
||||
Reference in new issue
Block a user