Skip to content

perf(router): negotiation-first ordering cuts the CM5 route 113min → 14min#680

Merged
ecto merged 2 commits into
mainfrom
claude/route-speed-negotiation-first
Jul 25, 2026
Merged

perf(router): negotiation-first ordering cuts the CM5 route 113min → 14min#680
ecto merged 2 commits into
mainfrom
claude/route-speed-negotiation-first

Conversation

@ecto

@ecto ecto commented Jul 25, 2026

Copy link
Copy Markdown
Owner

What

The greedy phase paid for its unrouted tail three times over.

  1. Every connection the speculative batch search missed got the full sequential rescue arsenal (flow escape, coupled-pair phantom, true shove) inline in the batch loop, one connection at a time.
  2. The GPU negotiator — which resolves that same tail in seconds — only ran afterwards.
  3. Then rip-up rounds re-solved it a third time, and the incremental negotiation rounds a fourth.

Measured on the full CM5, the baseline spends 82% of its 113 minutes on three rounds that all end below where they started and are then discarded wholesale by keep-best:

baseline phase cost outcome
route_batch (greedy + inline arsenal) 680.5s 500 conns → 37 unrouted
GPU negotiation ~10s 37 → 8 pending
rip-up round 2821.6s placed 536 → 511, discarded
incremental round 1 1218.4s placed 536 → 494, discarded
incremental round 2 1506.5s placed 536 → 475, discarded
post-loop repair / stitching ~550s

This reorders the layer-rich regime around the negotiator and lets the arsenal work only what negotiation leaves behind.

Changes

  • route_batch runs search-only when negotiation goes first. Failures fall straight through to the negotiator; the arsenal fires once afterwards over its leftovers (new arsenal_sweep). On the full CM5 negotiation takes pending 37 → 11 in 4.9s, the sweep 11 → 10 in 48s. Dropping the inline arsenal also cuts the greedy batch itself from 680.5s to 470.9s for identical output (191 committed, 37 unrouted).
  • Skip the greedy phase's rip-up rounds once negotiation has run first — that 47-minute round above, plus 8 minutes on the 250-net subset (382 → 380), both discarded.
  • Stop the negotiation loop after round 0 in this regime. Round 0 is already a negotiated pass carrying the arsenal and the last-round fine-retry that the trailing polish round exists to guarantee, so stopping forfeits nothing it could have added.
  • Make the surviving budgets binding. They were checked between connections only, but one connection's victim re-route is itself a whole batch — so a 92s budget measured 231s. route_batch now takes an optional deadline; past it the remaining queue is reported unrouted, which every caller already handles (a stuck connection stays pending, a ripped victim gets its original copper restored). It costs attempts, never legality.

Board-regime rule preserved

The >= 4 copper layers gate is unchanged and now lives in one place (negotiate_first_regime). Thin boards keep arsenal-first ordering, the full rip-up ladder, and all four negotiation rounds — their scarce corridors are spent more carefully by the arsenal (the VESC lost 4 points of routability to negotiation-first). Boards built without the gpu feature, or run without VCAD_GPU_NEGOTIATE=1, are untouched.

Benchmarks

Same machine, same session, background load ~5, VCAD_GPU_BATCH=1 VCAD_GPU_NEGOTIATE=1. CM5 before/after are both measured here, not quoted:

board before after
CM5 (10 layers, 522 conns) 6781.4s = 113.0 min @ 0.985 863.9s = 14.4 min @ 0.981 7.85× faster
VESC (thin board) ~26s @ 0.79 22.9s @ 0.787 unchanged
moteus (Eagle) 80–124s @ 0.87 107.6s @ 0.891 unchanged / slightly up

Where the CM5's 14.4 min goes now: route_batch 470.9s, GPU negotiation 4.9s, arsenal sweep 48.4s, post-loop repair and plane stitching the rest.

Routability trade: 0.985 → 0.981, i.e. 2 connections of 544, against 99 minutes. It is above the 0.98 bar but worth naming: in the old order the inline arsenal commits its rescues during the batch, so the board those rescues leave behind lets negotiation reach pending 8 where the new order reaches 10. Recovering them would mean running the sweep over all 37 failures before negotiation rather than over its 11 leftovers — roughly +2 minutes — if those two connections are worth it.

Verification

  • cargo test -p vcad-ecad-pcb — 248 passed
  • cargo clippy -p vcad-ecad-pcb --features gpu -- -D warnings — clean (also clean without the feature)

🤖 Generated with Claude Code

… 14min

The greedy phase paid for its unrouted tail twice. Every connection the
speculative batch search missed got the full sequential rescue arsenal
(flow escape, coupled-pair phantom, true shove) inline in the batch loop,
and the GPU negotiator — which resolves the same tail in seconds — only
ran afterwards. Then rip-up rounds re-solved it a third time at ~45
minutes each, routinely ending with fewer connections placed than they
started (536 -> 511) and being discarded wholesale by keep-best.

Reorder the layer-rich regime around the negotiator:

- route_batch runs search-only when negotiation goes first; failures fall
  straight through to the negotiator, and the arsenal fires once
  afterwards over its leftovers (`arsenal_sweep`). Measured on the full
  CM5: negotiation takes pending 37 -> 11 in 4.9s, the sweep 11 -> 10 in
  48s.
- Skip the greedy phase's rip-up rounds once negotiation has run first.
  Both measured rounds in this regime regressed and were thrown away
  (536 -> 511 full board, 382 -> 380 on the 250-net subset).
- Stop the negotiation loop after round 0 in this regime. Round 0 is
  already a negotiated pass carrying the arsenal and the last-round
  fine-retry, and every incremental CPU round measured here ended below
  it and was discarded (335s + 183s full board, 231s + 130s subset).
- Make the surviving budgets binding. They were checked between
  connections only, but one connection's victim re-route is itself a
  whole batch, so a 92s budget measured 231s. route_batch now takes an
  optional deadline; over it, the remaining queue is reported unrouted —
  which every caller already handles (a stuck connection stays pending, a
  ripped victim gets its original copper restored), so it costs attempts,
  never legality.

The >= 4 copper layer gate is preserved and now lives in one place
(`negotiate_first_regime`): thin boards keep arsenal-first, the full
rip-up ladder and all four negotiation rounds, because their scarce
corridors are spent more carefully by the arsenal (the VESC lost 4 points
to negotiation-first). Boards without the gpu feature or with
VCAD_GPU_NEGOTIATE unset are untouched.

Benchmarks (same machine, load ~5, VCAD_GPU_BATCH=1 VCAD_GPU_NEGOTIATE=1):

  board   before              after
  CM5     ~113 min  0.985     14.4 min  0.981
  VESC     ~26 s    0.79      22.9 s    0.787
  moteus  80-124 s  0.87      107.6 s   0.891

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chojiai

chojiai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Choji has used up today's reviews

Your team has reached the free plan's limit of 5 reviews per day, so Choji stepped aside on this pull request. That's a plan limit, not a reflection of the change itself.

Your free reviews reset tomorrow. To have Choji review every push without waiting, upgrade to Pro for unlimited reviews.

You're on the free plan · 5 reviews/day.

@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

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

4 Skipped Deployments
Project Deployment Actions Updated (UTC)
mecheval Ignored Ignored Jul 25, 2026 12:58pm
vcad Ignored Ignored Jul 25, 2026 12:58pm
vcad-docs Ignored Ignored Jul 25, 2026 12:58pm
vcad-mcp Ignored Ignored Jul 25, 2026 12:58pm

Request Review

@ecto
ecto merged commit 7e1649f into main Jul 25, 2026
14 checks passed
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.

1 participant