Skip to content

Conversation

@Abse2001
Copy link
Contributor

@Abse2001 Abse2001 commented Dec 6, 2025

Panel Board Routing Caching

This PR adds structural board matching to panels so identical boards can reuse routing.
Only the first board in each identical group performs autorouting; the rest skip routing and copy its circuitJson output.
A routing lifecycle was added to ensure duplicates wait until the template routing finishes before applying results.


How the Duplicate Routing System Works

The system is implemented across both the Panel and Board classes and executes in several phases.


Phase 1: Detect Identical Boards

Method: Panel.doInitialReactSubtreesRender

  1. The panel collects all <board> children.
  2. It calls findIdenticalBoardGroups() to group boards based on:
    • Normalized board props
    • Structural equality of child components
    • A deterministic propKey used for comparison and caching
  3. For each identical group:
    • The first board becomes the template
    • Remaining boards are marked as duplicates using markAsDuplicate(templateBoard)
  4. Each duplicate stores:
    • _isDuplicateBoard = true
    • _templateBoard = templateBoard

Phase 2: Skip Autorouting

Method: Board.doInitialPcbTraceRender

When the PcbTraceRender phase runs:

  • Template boards run autorouting normally
  • Duplicate boards:
    • Detect they are duplicates using isDuplicate()
    • Skip autorouting entirely
    • Mark the phase as dirty so they re-check again during updates

This prevents duplicate boards from starting unnecessary autorouting tasks.


Phase 3: Wait for Template Board

Method: Board.updatePcbTraceRender

Duplicates enter a wait loop:

  1. Each update, they check the template board’s _asyncAutoroutingResult
  2. If the template is still routing:
    • Duplicate stays dirty and waits
  3. Once the template routing completes:
    • Duplicate transitions to copying mode
    • Calls _applyTemplateCircuitJson()

This ensures duplicates only apply routing once the template is fully done.


Phase 4: Copy Template Routing

Method: Board._applyTemplateCircuitJson

When applying routing:

  1. The template’s PCB elements are extracted using extractBoardCircuitJson()
  2. These elements are translated (offset) to match the duplicate board’s position
  3. Elements are applied using applyBoardCircuitJson()
  4. The duplicate now mirrors the template routing exactly, but in its own coordinate space

This makes multiple identical boards render extremely fast without recomputing traces.


Data Flow Diagram

Panel.doInitialReactSubtreesRender
→ findIdenticalBoardGroups
→ mark duplicates
→ template board runs autorouting
→ duplicates skip routing
→ duplicates wait for template to finish
→ duplicates copy template circuitJson


Key Internal Properties

Owner Property Purpose
Panel _duplicateBoardToTemplate Maps duplicate → template
Board _isDuplicateBoard Flags the board as a duplicate
Board _templateBoard Stores the template reference
Board _asyncAutoroutingResult Signals when routing is finished

Result

Panels with repeated boards now route dramatically faster.
Three identical boards route the cost of one; ten identical boards route the cost of one, etc.
Snapshot tests confirm consistent circuitJson output and stable caching behavior.

@Abse2001 Abse2001 requested a review from seveibar December 6, 2025 23:16
@vercel
Copy link

vercel bot commented Dec 6, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
tscircuit-core-benchmarks Ready Ready Preview Comment Dec 8, 2025 1:02pm

@Abse2001 Abse2001 changed the title Implement Robust Identical-Board Detection and PropKey-Based CircuitJson Caching Implement Robust Panel Identical-Board Detection and PropKey-Based CircuitJson Caching Dec 6, 2025
getBoardPropKey,
extractBoardCircuitJson,
applyCircuitJsonToBoards,
} from "../../utils/panels/identical-board-caching"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not lib/components/normal-components/Panel/..., utils directories are generally an anti-pattern whenever they are specific (keep similar things together)

* 1. Props (excluding position and children)
* 2. Instance children (component names and their props)
*/
export function boardsAreIdentical(a: Board, b: Board): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

multiple exports again, idk why people keep breaking this rule

one export = one file

for (const group of identicalBoardGroups) {
const [firstBoard] = group
const propKey = getBoardPropKey(firstBoard)
const cached = this._cachedBoardCircuitJsonByPropKey.get(propKey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call it boardCacheKey

Copy link
Contributor

@seveibar seveibar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think you're actually caching the boards- i.e. each board is rendered then you just replace the circuit json later. There will still be a massive performance penalty. You might need to create a RootCircuit to independently render a board

Put an event listener on the circuit to ensure that e.g. the autorouter isn't being called multiple times

@seveibar
Copy link
Contributor

seveibar commented Dec 8, 2025

it's something like

circuit.on("autorouting:start", () => { autorouterCallsCount++ })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants