You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The inference-throughput gate in current inference-perf overlays is a fixed absolute full-node value, while the recipes are SKU-agnostic (matched only on service + accelerator, no node shape). On a node with materially fewer GPUs than the one the floor was calibrated against, the gate false-fails a perfectly healthy run.
Evidence
The floor is a fixed absolute full-node number that does not track node GPU count. All performance-enabled inference overlays pin >= 50000 tok/s, despite being calibrated on different node shapes:
recipes/overlays/h100-eks-ubuntu-inference-dynamo.yaml — measured on p5.48xlarge, 8 GPUs, throughput ~= 108,790 tok/s; floor >= 50000 (~46% of measurement).
recipes/overlays/gb200-eks-ubuntu-inference-dynamo.yaml — measured on p6e-gb200.36xlarge, 4 GPUs, throughput ~= 65,952 tok/s; floor >= 50000 (~76% of measurement).
b200-gke-cos-inference-dynamo.yaml has inference-perfdisabled pending a reference benchmark, so it is not affected today.
Because the value is a single absolute number rather than per-GPU, the same 50000 is asked of an 8-GPU H100 node, a 4-GPU GB200 node, and (via PR #1232) a 2-GPU AKS node alike.
The evaluator does not rescue smaller full nodes.scaledThroughputThreshold (validators/performance/inference_perf.go:135) only scales the gate down when fewer GPUs are benchmarked than the node exposes (gpuCount < gpuCountPerNode) — i.e. partial occupancy. gpuCountPerNode is discovered at runtime from the node's actual nvidia.com/gpu allocatable. On a full 2-GPU node gpuCount == gpuCountPerNode == 2 → no scaling → the full 50000 floor applies to 2 GPUs.
Sub-8-GPU H100 nodes exist on every provider (so the recipe will legitimately land on them):
Provider
Sub-8-GPU H100 shapes
AWS / EKS
p5.4xlarge (1x H100)
GCP / GKE
a3-highgpu-1g / 2g / 4g
Azure / AKS
NC40ads (1), NC80adis (2) — the SKU the repo's own docs/integrator/aks-gpu-setup.md documents
Linear estimate (extrapolated from the 8-GPU H100 baseline, ~13,600 tok/s/GPU):
Node
GPUs
Throughput (linear estimate)
Gate (50000 x 0.9 = 45,000)
p5.48xlarge etc.
8
~108,800 (measured)
pass
NC80adis
2
~27,200 (est.)
false fail
p5.4xlarge / NC40ads
1
~13,600 (est.)
false fail
The floor implicitly requires >= ~4 GPUs to be physically achievable.
Introduce a per-GPU constraint rather than redefining the existing one in place, so fixed-shape custom recipes that rely on the absolute-total semantics are not silently broken:
Add inference-throughput-per-gpu for SKU-agnostic recipes. The evaluator scales it by the number of GPUs actually benchmarked:
effectiveThreshold = perGPUFloor x gpuCount
Keep inference-throughput as the legacy absolute-total gate for fixed-shape recipes (unchanged semantics).
Reject a config that sets both for the same check (ErrCodeInvalidRequest) — they express conflicting intents.
The per-GPU form is sound because inference throughput is ~count-linear at fixed concurrency-per-GPU (the recipes already pin inference-concurrency-per-gpu: 256). A single per-GPU value then yields a correct gate on every node shape from one SKU-agnostic recipe:
Node
GPUs
inference-throughput-per-gpu = 6000 → gate (x0.9)
8-GPU
8
48,000 → 43,200
2-GPU
2
12,000 → 10,800
1-GPU
1
6,000 → 5,400
Each shape gets a proportional, achievable floor.
Implementation sketch
Add the inference-throughput-per-gpu constraint name to the inference-perf evaluator (validators/performance/inference_perf.go); document the per-GPU contract alongside the existing absolute inference-throughput.
When inference-throughput-per-gpu is present, require gpuCount > 0 (fail closed with ErrCodeInternal if result.gpuCount <= 0 — do not silently treat it as zero or pass through) and gate on perGPUFloor x gpuCount (correct under any occupancy — subsumes the partial-occupancy case scaledThroughputThreshold handles today). When inference-throughput is present, keep the current absolute/full-node-relative behavior. Reject both-set.
Migrate the SKU-agnostic inference overlays to inference-throughput-per-gpu with a conservative per-accelerator per-GPU value (H100, GB200, RTX Pro 6000 differ — keep the method consistent, values per accelerator).
Update inference-perf unit tests for the new constraint + scaling, and the both-set rejection.
Alternatives considered
Pin the benchmark to 1 GPU by default (fixed small workload, fixed per-GPU floor). Simpler and fully deterministic, but validates a synthetic 1-GPU shape rather than the deployed topology. Per-GPU-scaled-by-benchmarked-count is preferred because it gates the real deployment.
Per-SKU full-node overlays. Instead of normalizing one recipe to any node, make the recipe specific to the node (add SKU / GPU-count as a criteria dimension) so a measured full-node absolute floor is always valid for that shape.
Pros: most accurate gate — a real per-SKU baseline catches modest regressions, not just gross failures; no evaluator change (the current absolute compare already works); honest that a 2-GPU PCIe NC80 is a different perf class than 8-GPU SXM rather than "8 ÷ 4".
Cons: combinatorial explosion of overlays (reintroduces node shape on top of service × accelerator × os × intent × platform, each needing its own measured baseline); requires hardware access to calibrate every SKU — exactly the gap PR feat(recipe): add AKS H100 Dynamo perf check #1232 already calls out ("until Azure-specific baselines are available"), so it blocks on testbed time that the normalized approach does not; still needs the partial-occupancy down-scaling at the edges.
These approaches are not mutually exclusive: the per-GPU normalized floor is the default portable gross-failure gate that only needs a per-accelerator baseline, not per-SKU baselines; keeping the legacy absolute inference-throughput constraint (above) is the opt-in hook for a per-SKU overlay to override with a measured full-node number when someone has the testbed data and wants a tight regression gate for that specific shape.
Note on TTFT
inference-ttft-p99 is a per-request latency metric evaluated at fixed inference-concurrency-per-gpu. Because the benchmark keeps per-GPU load constant, TTFT is not expected to scale with node GPU count the way total throughput does. It is not the source of the SKU-count bug and does not need GPU-count normalization here. (TTFT can still vary with cloud SKU, CPU shape, frontend placement, model-load behavior, and request routing — so this is a statement about GPU-count normalization, not a portability guarantee.)
Scope / related
The training NCCL busbw gate has related but distinct node-shape sensitivity: it is topology/transport-class dependent, not count-linear. Track that in a separate issue.
PR feat(recipe): add AKS H100 Dynamo perf check #1232 adds the AKS inference perf phase by mirroring the existing sibling throughput floor. It should either defer the throughput floor for AKS (TTFT-only for now) or adopt the normalized throughput semantics once they land.
Summary
The
inference-throughputgate in currentinference-perfoverlays is a fixed absolute full-node value, while the recipes are SKU-agnostic (matched only onservice+accelerator, no node shape). On a node with materially fewer GPUs than the one the floor was calibrated against, the gate false-fails a perfectly healthy run.Evidence
The floor is a fixed absolute full-node number that does not track node GPU count. All performance-enabled inference overlays pin
>= 50000tok/s, despite being calibrated on different node shapes:recipes/overlays/h100-eks-ubuntu-inference-dynamo.yaml— measured on p5.48xlarge, 8 GPUs, throughput ~= 108,790 tok/s; floor>= 50000(~46% of measurement).recipes/overlays/gb200-eks-ubuntu-inference-dynamo.yaml— measured on p6e-gb200.36xlarge, 4 GPUs, throughput ~= 65,952 tok/s; floor>= 50000(~76% of measurement).>= 50000onh100-gke,h100-aks(PR feat(recipe): add AKS H100 Dynamo perf check #1232), andrtx-pro-6000-eks.b200-gke-cos-inference-dynamo.yamlhasinference-perfdisabled pending a reference benchmark, so it is not affected today.Because the value is a single absolute number rather than per-GPU, the same
50000is asked of an 8-GPU H100 node, a 4-GPU GB200 node, and (via PR #1232) a 2-GPU AKS node alike.The evaluator does not rescue smaller full nodes.
scaledThroughputThreshold(validators/performance/inference_perf.go:135) only scales the gate down when fewer GPUs are benchmarked than the node exposes (gpuCount < gpuCountPerNode) — i.e. partial occupancy.gpuCountPerNodeis discovered at runtime from the node's actualnvidia.com/gpuallocatable. On a full 2-GPU nodegpuCount == gpuCountPerNode == 2→ no scaling → the full50000floor applies to 2 GPUs.Sub-8-GPU H100 nodes exist on every provider (so the recipe will legitimately land on them):
p5.4xlarge(1x H100)a3-highgpu-1g / 2g / 4gNC40ads(1),NC80adis(2) — the SKU the repo's owndocs/integrator/aks-gpu-setup.mddocumentsLinear estimate (extrapolated from the 8-GPU H100 baseline, ~13,600 tok/s/GPU):
50000 x 0.9 = 45,000)p5.48xlargeetc.NC80adisp5.4xlarge/NC40adsThe floor implicitly requires >= ~4 GPUs to be physically achievable.
Proposed fix: single-GPU (per-GPU) normalized target
Introduce a per-GPU constraint rather than redefining the existing one in place, so fixed-shape custom recipes that rely on the absolute-total semantics are not silently broken:
inference-throughput-per-gpufor SKU-agnostic recipes. The evaluator scales it by the number of GPUs actually benchmarked:inference-throughputas the legacy absolute-total gate for fixed-shape recipes (unchanged semantics).ErrCodeInvalidRequest) — they express conflicting intents.The per-GPU form is sound because inference throughput is ~count-linear at fixed concurrency-per-GPU (the recipes already pin
inference-concurrency-per-gpu: 256). A single per-GPU value then yields a correct gate on every node shape from one SKU-agnostic recipe:inference-throughput-per-gpu = 6000→ gate (x0.9)Each shape gets a proportional, achievable floor.
Implementation sketch
inference-throughput-per-gpuconstraint name to theinference-perfevaluator (validators/performance/inference_perf.go); document the per-GPU contract alongside the existing absoluteinference-throughput.inference-throughput-per-gpuis present, requiregpuCount > 0(fail closed withErrCodeInternalifresult.gpuCount <= 0— do not silently treat it as zero or pass through) and gate onperGPUFloor x gpuCount(correct under any occupancy — subsumes the partial-occupancy casescaledThroughputThresholdhandles today). Wheninference-throughputis present, keep the current absolute/full-node-relative behavior. Reject both-set.inference-throughput-per-gpuwith a conservative per-accelerator per-GPU value (H100, GB200, RTX Pro 6000 differ — keep the method consistent, values per accelerator).inference-perfunit tests for the new constraint + scaling, and the both-set rejection.Alternatives considered
Pin the benchmark to 1 GPU by default (fixed small workload, fixed per-GPU floor). Simpler and fully deterministic, but validates a synthetic 1-GPU shape rather than the deployed topology. Per-GPU-scaled-by-benchmarked-count is preferred because it gates the real deployment.
Per-SKU full-node overlays. Instead of normalizing one recipe to any node, make the recipe specific to the node (add SKU / GPU-count as a criteria dimension) so a measured full-node absolute floor is always valid for that shape.
service × accelerator × os × intent × platform, each needing its own measured baseline); requires hardware access to calibrate every SKU — exactly the gap PR feat(recipe): add AKS H100 Dynamo perf check #1232 already calls out ("until Azure-specific baselines are available"), so it blocks on testbed time that the normalized approach does not; still needs the partial-occupancy down-scaling at the edges.inference-throughputconstraint (above) is the opt-in hook for a per-SKU overlay to override with a measured full-node number when someone has the testbed data and wants a tight regression gate for that specific shape.Note on TTFT
inference-ttft-p99is a per-request latency metric evaluated at fixedinference-concurrency-per-gpu. Because the benchmark keeps per-GPU load constant, TTFT is not expected to scale with node GPU count the way total throughput does. It is not the source of the SKU-count bug and does not need GPU-count normalization here. (TTFT can still vary with cloud SKU, CPU shape, frontend placement, model-load behavior, and request routing — so this is a statement about GPU-count normalization, not a portability guarantee.)Scope / related
busbwgate has related but distinct node-shape sensitivity: it is topology/transport-class dependent, not count-linear. Track that in a separate issue.TTFT-only for now) or adopt the normalized throughput semantics once they land.