Skip to content

estimate CLI, plus two opt-in engine corrections - #1

Merged
bhaktatejas922 merged 5 commits into
mainfrom
cli
Sep 11, 2026
Merged

bhaktatejas922 merged 5 commits into
mainfrom
cli

Conversation

@bhaktatejas922

Copy link
Copy Markdown

Adds the estimate command line over the existing engine, so sweeps and pinned configurations can live in other repos instead of throwaway scripts, and corrects the two places the simulator misleads. Both corrections are opt-in: engine defaults are unchanged and every upstream suite passes untouched.

The CLI

src/cli, bundled by esbuild into one dependency-free dist/cli/estimate.mjs (node cannot run the sources directly under moduleResolution: bundler).

estimate models
estimate chips --model k3
estimate search k3 gb300-nvl72 32x1 --slo 20 --top 3
estimate top k3 b300 8x1 --phase prefill
estimate explain k3 b300 8x1 --sizes TP=8,EP=8
estimate sweep --model k3 --chips b300,gb300-nvl72 --machines 8x1,32x1 --slos none,20 --table
estimate report <dir>

search streams a line per priced tuple to stderr and the ranked table to stdout; --json emits one row per line. explain scores one explicit sharding through the search's own scorer and prints the memory split, the operating batch, bound-by, and the vLLM/SGLang flags the roles spell as. sweep runs one worker thread per chip so a chip's warm reshard-plan caches stay put. scripts/search.ts is deleted, superseded by search --overlap 0,0.

Correction 1: reserved state slots

memoryFootprint charged one recurrent state per resident sequence, the physics floor. Real hybrid KV managers reserve several per request (SGLang: 3 with the radix cache off, 4 with extra_buffer_lazy, 5 with extra_buffer) plus one per speculative draft token. On Kimi K3 that pool, not the paged cache, caps concurrency, so the simulator admitted about 4x what an 8x B300 node measures.

EvalOptions.statePool {slotsPerSeq, specSlots, stateDtypeBytes} threads into the three capacity gates. MemoryFootprint now also reports the paged and state parts and the slot count. At the default the arithmetic is bit-identical to before. Five slots at a 0.9 static fraction gives 117 resident sequences against a measured 101 on that cell.

Correction 2: overlap from the op graph

The naive backend hides two hand-set fractions of the memory and comms streams behind the widest one; nothing in the trace informs them, and they are the largest lever on every number the tool shows. makeDagScheduleBackend keeps the same per-op prices and runs each segment as a software-pipelined loop over its repeats: the period is the larger of the fullest stream and the longest dependency chain, and the first iteration also waits for its weights. Time always lands in [widest stream, serial sum]; busy and busyPerOp are the naive backend's op for op, so bound-by and the DAG view keep working.

makeCostBackend({memoryOverlap, commsOverlap, scheduler}) selects, defaulting to naive. The explorer gains an overlap source picker, and the step-time bar draws a scheduled trace's own visible split.

Also

  • gb300-nvl72, an estimated entry whose header comment derives every number and says what would replace it.
  • src/ui/machines.ts moves to core/hardware/machines.ts (plus parseMachine), and the worker's toRow becomes optimizer/enrich.ts, so CLI numbers are the explorer's numbers rather than a second implementation of them.

Verification

npm run typecheck, npm run format:check, npm run bench, and the full suite (127 tests, 19 files) pass. New suites: tests/state-slots.test.ts, tests/dagSchedule.test.ts, tests/cli.test.ts. CI also builds the bundle and smoke-runs it.

Every number the tool emits is an estimate from a static roofline model. The two corrections move in the right direction against two measured points; they are not a calibration.

🤖 Generated with Claude Code

bhaktatejas922 and others added 5 commits September 11, 2026 01:50
Derived from the B300 entry the way GB200 derives from B200; the header
comment spells out each number so the datasheet can replace it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
memoryFootprint charged one recurrent state per resident sequence, the
physics floor. Hybrid KV managers reserve several per request (SGLang: 3
to 5 depending on the radix-cache strategy) plus speculative slots, and
on Kimi K3 that pool, not the paged cache, caps concurrency: the
simulator admitted 4x what an 8x B300 node measures.

EvalOptions.statePool {slotsPerSeq, specSlots, stateDtypeBytes} threads
into the three capacity gates; the default is bit-identical to before.
MemoryFootprint now also reports the paged and state parts and the slot
count. Five slots at 0.9 static fraction lands within 16% of the
measured 101-request ceiling.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The naive backend hides two hand-set fractions of the memory and comms
streams behind the widest one; nothing in the trace informs them and
they are the largest lever on every number shown. The dag backend keeps
the same per-op prices and runs each segment as a software-pipelined
loop over its repeats: the period is the larger of the fullest stream
and the longest dependency chain (each op as wide as its slowest
stream), the first iteration also waits for its weights. Time always
lands in [widest stream, serial sum]; busy and busyPerOp are the naive
backend's op for op, so bound-by and the DAG view keep working.

makeCostBackend({memoryOverlap, commsOverlap, scheduler}) selects; the
default stays naive. The UI gains an overlap source picker and the
step-time bar draws a scheduled trace's own visible split.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
machines.ts becomes core/hardware/machines.ts (plus parseMachine for a
machine spelled on a command line) and the worker's toRow becomes
optimizer/enrich.ts enrichCandidate, returning a ConfigResult the UI's
row type extends. A Candidate now carries the backend that priced it,
and the search's per-placement scorer is exported. No behavior change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The explorer's pipeline, scriptable: models and chips tables, a streaming
sharding search with a ranked top-N, explain for one worked sharding
(memory split, batch, step time, bound-by, and the vLLM/SGLang flags the
roles spell as), a threaded sweep over chips x machines x phases x SLOs,
and report to re-render its rows. --json emits one row per line.

The two engine corrections are flags, off by default: --state-slots (and
--spec-slots, --state-dtype, --mem-fraction) for the recurrent-state
slots a hybrid KV manager holds per sequence, --scheduler dag to read
overlap off the op graph. scripts/search.ts is gone, superseded.

esbuild bundles src/cli into one dependency-free dist/cli/estimate.mjs;
node cannot run the sources directly under moduleResolution bundler.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bhaktatejas922
bhaktatejas922 merged commit 1afe874 into main Sep 11, 2026
2 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