Repository navigation
perf(layout): keep Smart Layout's candidate scoring off the main thread - #105
Merged
Merged
Conversation
On the bundled sample model the ELK candidate phase blocked the renderer for ~3.4 s. Almost all of it was minimizeCrossings (300–400 ms per candidate × 10), not ELK itself. - minimizeCrossings scores each swap/shift by the pairs it can change instead of re-scoring the whole group. Costs are exact integer counts, so decisions are identical; ~6x faster (also speeds up Radical Layout, which calls it on the main thread). - The main thread now only runs the layout engines. Crossing minimisation, finalising and scoring of every candidate move into the Smart Layout worker (runSmartLayoutWorkerPhase), with a "Scoring layouts… N/10" progress stage. - Engines run one at a time with a yield in between: bundled ELK lays out synchronously, and ten queued layouts ran as one uninterrupted block. Co-Authored-By: Claude Opus 5.5 <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.
Follow-up to #104.
Summary
On the whole bundled sample model, Smart Layout's ELK candidate phase blocked the renderer for ~3.4 s. Profiling showed almost all of that was
minimizeCrossings(300–400 ms per candidate × 10), not ELK itself.minimizeCrossings. Each swap or shift is scored by only the pairs it can change (localCost), instead of re-scoring the whole group before and after. Costs are exact integer counts, so every accept/reject decision is the same as before.minimizeCrossingson the main thread in the store.RawCandidate). Crossing minimisation, finalising and scoring of every candidate moved into the Smart Layout worker.runSmartLayoutSAPhasebecamerunSmartLayoutWorkerPhase, and the button shows a new "Scoring layouts… N/10" stage.Results
Measured against #104 (
ac1a105). "Longest block" is the longest gap between 1 ms event-loop ticks.Follow-up (not in this PR)
ELK itself still runs on the main thread, which accounts for the remaining ~80 ms cold block. Running it in its own worker (
elk-api+elk-worker.min.js) would remove that, but it depends on how Vite/Electron serve the worker script and needs checking in the app.Test plan
npm run typechecknpm teston top of currentmain: 432 passed, 2 of them new. One checks thatminimizeCrossingsnever raises the cost; the other checks that the ELK phase only runs the engines, one at a time, reporting each.minimizeCrossingsoutput identical on 55 inputs.🤖 Generated with Claude Code