Repository navigation
perf(memory-graph): fix O(n^2) BFS queue drain in computeClusterAssignments - #1714
Open
iam-saiteja wants to merge 3 commits into
Open
iam-saiteja wants to merge 3 commits into
iam-saiteja wants to merge 3 commits into
Conversation
Verifies the O(n^2) queue.shift() bottleneck in the BFS used to cluster memory-graph nodes, ahead of fixing it in a follow-up commit.
computeClusterAssignments drained its BFS frontier with Array.shift(), which is O(remaining length) per call. Wide-frontier components (e.g. a hub memory that many others relate to) made the drain O(n^2) on every memory-graph load/update. Swapped to an index-pointer dequeue, which preserves identical traversal order and output.
The Bun-based benchmark alone is misleading: Bun's JavaScriptCore engine optimizes Array.shift() well enough that it shows almost no difference between the old and new implementation, even though this component runs in users' browsers (predominantly V8), where the gap is real and grows with graph size. Added bench-cluster-assignments-v8.mjs, a standalone zero-dependency script runnable with plain `node`, so the V8 numbers are independently reproducible rather than just asserted. Both benchmarks now print a warning when run under the wrong engine and go up to 100k nodes.
Author
|
Hi @MaheshtheDev @Dhravya, whenever you get a chance, could one of you take a look at this PR? It's a small, self-contained perf fix: There's a regression test, and all checks pass locally (198/198 tests, types and biome clean). Happy to adjust anything. Thanks! |
This branch has not been deployed
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.
Summary
computeClusterAssignments(groups memory-graph nodes into visual clusters) drained its BFS frontier withArray.shift(), which is O(remaining length) per call. A single wide-frontier connected component — e.g. a "hub" memory that many othersderives/updatesfrom — made the whole traversal O(n^2).shift()dequeue with an index-pointer dequeue (head++). Output is byte-identical; only the dequeue mechanism changed (3 lines of actual logic).Why this matters
computeClusterAssignmentsruns inside auseMemokeyed on the fulldocumentsarray, so it re-executes on the whole dataset every time the memory graph loads or its data changes, on the browser main thread.Benchmarks
bun run scripts/bench-cluster-assignments.tsimports the live function and keeps a frozen pre-fix snapshot for comparison, plus a correctness check.node scripts/bench-cluster-assignments-v8.mjsis a zero-dependency, engine-independent companion. This matters because this repo's dev tooling runs on Bun (JavaScriptCore), which optimizesArray.shift()much better than V8 — the engine Chrome/Edge/most browsers (i.e. this component's actual users) run. Under Bun the gap barely shows; under V8 it doesn't disappear. Both scripts print a warning if run under the wrong engine.Averaged over 10 independent process runs on Node/V8 (not just samples within one run, to rule out GC/JIT noise):
The old implementation is also far less consistent: standard deviation ~40% of its mean at 80k, vs. ~15% for the fix. So beyond the average speedup, this also removes a source of unpredictable multi-second freezes for users with large, densely cross-referenced memory graphs.
Test plan
bun run test(packages/memory-graph) — 198/198 passingbun run check-types— cleanbiome check— clean (this Windows checkout shows repo-wide CRLF-vs-LF diffs from localcore.autocrlf=true; verified that's pre-existing/unrelated by checking an untouched file)