perf: make proposal clustering linear in SubgraphSampler - #783
Merged
Merged
Conversation
set_proposal_clusters accumulated its "visited" set with "visited = visited.union(cluster)", which allocates a fresh copy of every proposal seen so far on each of the ~n cluster iterations. The routine runs in SubgraphSampler.__init__, before the first batch is yielded, so whole-brain inference stalled at 0/N with no progress. At 2.3M proposals (~1.44M clusters) this is ~1.6e12 element copies. Measured on the same pattern in isolation: 0.30s at n=10k, 1.39s at 20k, 6.35s at 40k, 27.05s at 80k -- clean quadratic, extrapolating to roughly 6 hours per inference round at 2.3M. Mutating in place with "visited.update(cluster)" is equivalent, since "visited" is only read by the "proposal not in visited" membership test. The full routine then runs in 7.3s at 2.3M proposals and scales linearly (0.14s at 50k, 0.79s at 200k, 3.39s at 800k). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Problem
Whole-brain split inference stalls at
Inference: 0%| | 0/2304627and never emits a batch.SubgraphSampler.set_proposal_clustersaccumulated itsvisitedset by rebinding:set.union()allocates a new set containing everything seen so far, once per cluster. With ~2.3M proposals forming ~1.44M clusters, that is roughly 1.6e12 element copies.This runs in
SubgraphSampler.__init__, before the first batch is yielded, and the sampler is constructed inside the dataset's producer thread — so the progress bar sits at 0 for the entire time with no indication of work. It also recurs on every inference round, since the sampler is rebuilt each round.It went unnoticed because at block-level scales (tens of thousands of proposals) it costs a second or two.
Measurements
The pattern in isolation, union vs. in-place update:
visited.union(...)visited.update(...)Clean quadratic — 4x the time for 2x the input. Extrapolated to 2,304,627 proposals: ~6 hours per inference round.
Fix
Equivalent, since
visitedis only read by theproposal not in visitedmembership test — contents are identical, only the allocation behavior changes.Full
set_proposal_clustersafter the fix, benchmarked against a synthetic proposal graph with realistic cluster sizes:Linear. ~7 s instead of ~6 h.
Notes
src/for the same accumulate-by-copy pattern. The only other.unionin a comparable position isproposal_graph.py:333, which is a single call outside a loop and is fine.merge_proposalscallsrx.graph_has_pathper above-threshold proposal, each a fresh traversal of the full graph. That is the next likely bottleneck at whole-brain scale, but it is pre-existing behavior and separate from this stall.tests/contains only__init__.py.🤖 Generated with Claude Code