analytical-viz: add sidebar Batch B selectbox + wire through all suggesters

New sidebar widget in the Workload expander:
  Batch B (concurrent requests) — selectbox 1/2/4/…/256, default 1.

Wired end-to-end:
  - app.py sidebar → forwards b_batch when constructing the main topo
  - auto_suggest(..., b=1) — passes b to _score_candidate, which builds
    TopologyConfig with b=b
  - run_auto_explore(..., b=1) — sets topo.b = b for every enumerated
    candidate before scoring
  - joint_explore(..., b=1) — forwards b to _best_parallelism_for_hw
    and _best_parallelism_two_stage; both set topo.b = b before scoring

All button handlers (sidebar Apply-scope, Physical Layout scope,
Auto Suggest tab sweep, Auto Hardware tab sweep) now pass b_batch.

Combined with the earlier partial-scaffold commits (memory_layout
scales KV by B; stage_rmsnorm / stage_wq / stage_wkv / stage_kv_append /
_per_hop_qkT_pv scale flops + activation memory by B), changing B in
the sidebar now affects reported latency and per-PE memory footprint
in the visible parts of the pipeline. The remaining FFN + comm-AR
stages still ignore B (they'll be next); their contribution is small
for the memory-bound decode case that matters most, but latency for
FFN-heavy configs at high B will be slightly under-reported until
those are scaled too.

Verified: 24 pytest tests pass at default B=1 (backward compat).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 22:51:56 -07:00
parent 674e194dc9
commit fd45bd2408
4 changed files with 35 additions and 12 deletions
@@ -237,6 +237,7 @@ def _best_parallelism_for_hw(
model: ModelConfig, machine: MachineParams,
s_kv: int, mode: str,
include_attention: bool = True, include_ffn: bool = True,
b: int = 1,
) -> ConfigScore | None:
"""Return the latency-minimum feasible parallelism for this HW.
@@ -247,6 +248,7 @@ def _best_parallelism_for_hw(
best: ConfigScore | None = None
best_key: tuple | None = None
for topo in _iter_reduced_parallelism(model, s_kv, mode):
topo.b = b
cfg = FullConfig(model=model, topo=topo, machine=machine)
s = score_config(cfg, include_attention=include_attention,
include_ffn=include_ffn)
@@ -265,13 +267,14 @@ def _best_parallelism_two_stage(
model: ModelConfig, machine: MachineParams,
s_kv: int, mode: str,
include_attention: bool = True, include_ffn: bool = True,
b: int = 1,
) -> ConfigScore | None:
"""Fast fallback: use autosuggest's memory-min (CP,TP,PP) then score it."""
sug = auto_suggest(model, machine, s_kv, mode)
if not sug.fits:
return None
topo = TopologyConfig(
cp=sug.cp, tp=sug.tp, pp=sug.pp, dp=1,
cp=sug.cp, tp=sug.tp, pp=sug.pp, dp=1, b=b,
s_kv=s_kv, mode=mode,
kv_shard_mode="split",
ffn_shard_scope="TP+CP",
@@ -366,6 +369,7 @@ def joint_explore(
depth: str = "balanced",
include_attention: bool = True,
include_ffn: bool = True,
b: int = 1,
) -> JointExploreResult:
"""Sweep HW candidates × parallelism, return joint Pareto + sensitivity.
@@ -383,11 +387,13 @@ def joint_explore(
par = _best_parallelism_two_stage(
model, machine, s_kv, mode,
include_attention=include_attention, include_ffn=include_ffn,
b=b,
)
else:
par = _best_parallelism_for_hw(
model, machine, s_kv, mode,
include_attention=include_attention, include_ffn=include_ffn,
b=b,
)
if par is None:
continue