Skip to content

feat(explore): route EXPLORE (and SWEEP) through InferaSim instead of booting every candidate - #1494

Open
araina-amd wants to merge 14 commits into
mainfrom
araina/dev/infersim-benchmark-backend
Open

araina-amd wants to merge 14 commits into
mainfrom
araina/dev/infersim-benchmark-backend

Conversation

@araina-amd

Copy link
Copy Markdown
Collaborator

Summary

Wire InferaSim into Hyperloom as the EXPLORE benchmark backend so candidate configs are projected instead of standing up a full serving engine for every point. EXPLORE currently boots a real server per candidate; across the 9 shipped workloads that is 491 GPU-hours per search, almost all of it engine init rather than the benchmark. InferaSim already answers the same question (TTFT / TPOT / throughput / KV fit) from a short serving anchor plus an analytical transport.

The path is intended to be opt-in:
HYPERLOOM_BENCHMARK_BACKEND=inferasim

pointed at the warmup/anchor store. Magpie remains the default; this does not change FRAMEWORK_AGENT or KERNEL_AGENT (those change kernels, so each candidate is its own regime and projection saves nothing).

Why

From one real candidate artifact (openai/gpt-oss-120b, TP=8, ISL 1024 / OSL 128, conc 64): 421 s wall, of which engine init (memory profiling, KV alloc, CUDA-graph capture) dominates. Weight load is ~4.5 s. Changing TP/EP/PP pays that again. 60 candidates × 8 GPUs × 421 s ≈ 56 GPU-hours for one workload.

Those 60 configs differ in batch, parallelism and concurrency — exactly the axes InferaSim transports from one measured decode-step ladder.

What's included

  • Backend dispatch: HYPERLOOM_BENCHMARK_BACKEND=inferasim on the same benchmark call the grid runner already makes (today: Magpie / bypass).
  • One serving anchor per regime, not per candidate. Short run at min(TP, 4) GPUs (~150 s boot + 72 s client = 222 s on 4 GPUs). Measured decode steps are the projection level; the model supplies deltas for other batch / TP / concurrency. For Arbor's shipped grid that is 8 anchors, not 60 servers.
  • Regime-scoped anchor store. An anchor is valid only for the kernels it was taken on (AITER vs Triton on gpt-oss-120b is a constant ~1.7×). Scheduling levers (maxseqs, chunked-prefill) reuse an existing anchor; kernel levers (aiter*, attention backend, cudagraph, KV dtype) require their own.

Validation (from the InferaSim-in-Hyperloom measurement)

Held-out decode step vs a real server, 40 points / 2 models:

scored run pts mean worst bias
gpt-oss-120b vLLM TP1 mxfp4 9 7.7% 20.3% +7.1%
gpt-oss-120b vLLM TP2 mxfp4 9 9.3% 14.3% +4.8%
gpt-oss-120b vLLM TP4 mxfp4 9 8.2% 16.0% +3.7%
gpt-oss-120b vLLM TP8 mxfp4 9 4.0% 10.0% −0.2%
DeepSeek-R1 vLLM TP8 fp8 4 6.4% 10.1% −2.0%
pooled 40 7.2%

TP8 gpt-oss-120b projected from a TP=4 anchor: c8 6.13 vs 6.04 ms (−1.5%), c32 7.21 vs 7.01 ms (−2.9%) — the 2.2% “against a real server” figure.

GPU-hours on Hyperloom's 9 shipped workloads (60-candidate EXPLORE grid):

GPU-h today GPU-h with projection speedup
8-GPU jobs (gpt-oss, DeepSeek, Qwen3-8B TP8, …) 56.1 4.77 11.7×
1-GPU jobs 7.0 0.84 8.3×
16-GPU two-node / rayjob 112.2 7.58 14.8×
all 9 pooled 491 39.3 12.5×

Of the remaining 39.3 GPU-h, 24.5 are the three real validation runs per workload and 14.8 are anchors. Costless anchors would be 20× — that is the ceiling, set by how much real measurement we keep, not by the projector.

Attribution (pooled): reuse one anchor per regime is 5.73× / 94% of the saving; reduced-parallelism anchors +1.71×; short sweep vs full run +1.34×. Projecting without reuse saves nothing.

Notes / limitations

  • 12.5× is a GPU-hour result on the EXPLORE grid, not session wall clock. FRAMEWORK + KERNEL remain 55% of the 2-hour budget and are unprojectable.
  • Pooled 7.2% mean error is not flat: it peaks near batch 64 (+9.8%) and is small at both ends; worst single point is 20.3%.
  • Breadth is still thin (2 models, 40 points). Do not headline “inside 10% everywhere.”
  • PRELUDE already boots a real server — emitting the first-session anchor from it (instead of re-booting) is a further ~1.8 GPU-h and should be a follow-up, not a blocker.

Test plan

  • Default path unchanged: unset HYPERLOOM_BENCHMARK_BACKEND still uses Magpie
  • HYPERLOOM_BENCHMARK_BACKEND=inferasim with an empty anchor store harvests one short serving anchor per regime, then scores the rest of EXPLORE without a full engine boot
  • Kernel-changing candidates (AITER on/off, attention backend, cudagraph, KV dtype) do not reuse a mismatched anchor (regime signature must differ)
  • Scheduling-only candidates (maxseqs, chunked-prefill) reuse the regime anchor
  • Keep-or-revert still requires a real measured validation run (projection never decides a keep)
  • Optional: SWEEP concurrency ladder projected from the same EXPLORE anchor, then spot-check 2–3 points on a live server

araina-amd and others added 9 commits September 11, 2026 20:10
Projects a candidate's serving metrics analytically and returns the same
benchmark_report.json contract, so real GPUs are only needed where the kernel
regime changes. An anchor is reused across transport axes (TP/EP/PP, batch,
concurrency, sequence lengths); a dtype or attention-backend change is flagged
as needing a fresh one.

Real-weights anchors rank ahead of dummy-weight ones, whose synthetic MoE
routing flattens the decode curve: projected TPOT 30.7% -> 2.4% MAPE against
measured serving for gpt-oss-120B.

Co-authored-by: Cursor <cursoragent@cursor.com>
Three ways the backend could answer confidently from an anchor that did not
describe the candidate.

Speculative decoding was not parsed, so every mtp variant inherited a
plain-decode anchor -- worth 33-91% on DeepSeek-R1. All three framework
spellings now land on the regime axis. Corrupt anchors were selectable:
decode latency cannot fall as batch rises, and 18 of 62 artifacts on this
cluster fail that check, so they are rejected in favour of pure analysis.
DeepSeek-R1, DeepSeek-V2-Lite and MiniMax-M2 had no preset mapping.
The gate reads whatever JSON sits in an anchor directory, and analysis output
lives alongside artifacts. A top-level list or scalar reached doc.get("sweep")
and raised AttributeError out of select_anchor instead of being rejected.
The filter compared spec.model_path, a checkout path, against the artifact's
HuggingFace id. Those never match, so it selected nothing and then fell back
to the full entry list -- any workload could calibrate against any anchor.
Three different models returned the same decode step to two decimal places.

Names now go through regime.models_match, shared with the Infera launcher
rather than duplicated. An anchor with no recorded model is still allowed,
since a structural warmup names no checkpoint.
A target arrives with a checkout path and a preset, and either one identifies
it. Matching only on the preset rejected DeepSeek-R1's own anchor, since
deepseek_v3 shares no spelling with its checkpoints; matching only on the path
would miss a bare preset name. Both are now tried.
The first of each variant's two benchmarks exists to leave the server hot and
run the accuracy gate; its throughput is discarded. It still runs at full
length, five to ten waves of the concurrency.

One wave reaches steady state. Across six boots and 54 seed-to-seed
comparisons the first pass lands 0.97% faster than later ones -- the opposite
of warming, and inside the run-to-run spread. Alongside it, a variant already
benchmarked against this exact stack is not re-measured unless its verdict sits
within two noise envelopes of the KEEP threshold.

Gated by INFERENCE_OPTIMIZER_EXPLORE_SHORT_WARMUP; an unreadable config falls
back to today's warmup.

Co-authored-by: Cursor <cursoragent@cursor.com>
A kernel occupying f of GPU time and made S times faster cannot move
end-to-end throughput past 1/((1-f) + f/S). When that ceiling is below the KEEP
threshold, the integrate run boots a server to confirm arithmetic.

The gate carries a 1.5x margin on the measured GPU share, so a mis-attributed
profile does not discard a real win; unreadable share or speedup still runs the
benchmark.

Gated by HYPERLOOM_KERNEL_AMDAHL_GATE.

Co-authored-by: Cursor <cursoragent@cursor.com>
The projection is validated against real vLLM measurements out to 65,536
tokens of context and never across a node boundary, but past either line it
still returns a confident number -- the last such extrapolation hid a 46.3%
error until the sweep was run.

Projections now carry notes naming the axis being extrapolated along. These are
deliberately not error bars: the size of the error is the unknown.

Co-authored-by: Cursor <cursoragent@cursor.com>
_explore_screen.py adds an optional reduced-scale probe that can drop variants
before the round benchmarks them on the deployment. Off by default, it only
prunes, and prunes nothing unless probe and deployment agree on the kernels
they resolved -- the same flags do not give the two engines the same stack.

The EXPLORE plateau exit could not fire: its gain arm summed over
winners_history, where every entry had already cleared KEEP, and its streak arm
counted rounds that proposed nothing rather than rounds that kept nothing.
Gated by HYPERLOOM_EXPLORE_PLATEAU_ROUND_WINDOW.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread src/hyperloom/common/codex_session.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/prompt_builder.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/prompt_builder.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/prompt_builder.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/prompt_builder.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/prompt_builder.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/prompt_builder.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/prompt_builder.py Fixed
Comment thread src/hyperloom/orchestrator/prompts/specialist_prompt_builder.py Fixed
araina-amd and others added 2 commits September 11, 2026 20:13
Magpie's client-only path is env-driven, but Hyperloom emitted that env only
for multi-node runs, so a single-node run could not target an externally
hosted engine -- an Infera router fronting vLLM/SGLang, say. The gate was
is_multi_node(), standing in for whether the server is ours to manage;
uses_external_server() asks that directly, keyed on
HYPERLOOM_MN_EXT_SERVICE_URL.

_kill_stale_servers, recover's kill stage and both server_lifecycle
eligibility checks now decline, since each would otherwise reap or reuse the
very server being measured. Verified at unit level only.

Co-authored-by: Cursor <cursoragent@cursor.com>
Match the InferaSim CLI and the INFERASIM_* env prefix. The selector is now
HYPERLOOM_BENCHMARK_BACKEND=inferasim.

Co-authored-by: Cursor <cursoragent@cursor.com>
@araina-amd
araina-amd force-pushed the araina/dev/infersim-benchmark-backend branch from 91fc101 to e51c3de Compare September 11, 2026 20:25
Comment thread src/hyperloom/orchestrator/actions/executors/_explore_screen.py Fixed
Comment thread src/hyperloom/inference_optimizer/tests/test_explore_screen.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_runner.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_runner.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_runner.py Fixed
Comment thread src/hyperloom/orchestrator/phases/machine_state.py Fixed
Comment thread src/hyperloom/orchestrator/phases/machine_state.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_bridge.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_bridge.py Fixed
Comment thread src/hyperloom/inference_optimizer/tests/test_explore_screen.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/_explore_screen.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_bridge.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_bridge.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_runner.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_runner.py Fixed
Comment thread src/hyperloom/orchestrator/actions/executors/inferasim_runner.py Fixed
Comment thread src/hyperloom/orchestrator/phases/machine_state.py Fixed
Comment thread src/hyperloom/orchestrator/phases/machine_state.py Fixed
araina-amd and others added 3 commits September 15, 2026 22:34
The short-warmup path still named run_gv from the pre-rebase tree, so ruff
F821 failed and the explore executor tests never reached Magpie. Also runs
ruff format over the files this branch adds, and clears the CodeQL notes
(unclosed files, duplicate json imports, unused constants).

Co-authored-by: Cursor <cursoragent@cursor.com>
InferaSim made the engine a regime axis, because one checkpoint's vLLM and
SGLang anchors were hashing to a single signature. We build the recipe
ourselves and did not set it, and an axis missing on either side scores as
matching, so a vLLM anchor and an SGLang anchor both came back at distance 0
-- the mismatch the branch already rejects for dtype and attention backend.
The spec has carried the framework all along.

Co-authored-by: Cursor <cursoragent@cursor.com>
select_anchor was the only thing keeping a vLLM measurement away from an
SGLang candidate: the projection never named its engine, so Infera's own
regime gate saw the axis as unset and matched as before. Passing
--serving-engine closes that on the --load-benchmark path, where calibration
is what the anchor is being read for.

Nothing in the analytical model reads it, so no projected number moves. An
unnamed framework still omits the flag rather than sending a blank.

Co-authored-by: Cursor <cursoragent@cursor.com>
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