SGLang inference benchmark configuration for Cluster Validation Suite (CVS)#
2026-10-27
12 min read time
CVS ships three SGLang inference suites for AMD MI30x and MI35x clusters. Each suite reads a JSON
configuration file from cvs/input/config_file/inference/sglang/ and a matching
threshold file referenced by top-level threshold_json. Hosts come from cluster.json
node_dict order. Role lists in the workload JSON are ignored.
CVS suite |
Test module |
Topology |
|---|---|---|
|
One unified |
|
|
One unified multi-node server. The first |
|
|
Disaggregated prefill/decode. The first even |
See Run SGLang LLM inference benchmarks with Cluster Validation Suite (CVS) for more information on running these tests.
Run any suite with:
cvs run <suite> \
--cluster_file cvs/input/cluster_file/<cluster>.json \
--config_file cvs/input/config_file/inference/sglang/<config>.json
Copy a template locally:
cvs config list inference/sglang
cvs config copy inference/sglang/mi3xx_sglang_llama_70b_single.json \
--output ~/cvs_workspace/inference/sglang/mi3xx_sglang_llama_70b_single.json
cvs config copy inference/sglang/mi325_sglang_llama_70b_threshold.json \
--output ~/cvs_workspace/inference/sglang/mi325_sglang_llama_70b_threshold.json
Note
{user-id}in path strings is resolved to the current username at runtime.Replace every
<changeme>placeholder before running; unresolved placeholders cause a hard exit at startup.
Configuration files#
All SGLang templates live under cvs/input/config_file/inference/sglang/.
Model and topology templates#
Templates are named <gpu>_sglang_<model>_<topology>.json. <model> is
llama_70b, deepseek_r1_0528, or qwen35_397b. <topology> is single,
distributed, or disaggregated.
Config prefix |
Threshold prefix |
GPU family |
|---|---|---|
|
|
MI30x |
|
|
MI35x |
Topology suffix |
Suite |
|---|---|
|
|
|
|
|
|
Threshold files#
Performance cells and pass/fail limits are stored separately. Each workload points at its
threshold file through top-level threshold_json (a filename beside the config).
Threshold file |
Referenced by |
|---|---|
|
MI30x Llama 3.1 70B configs (single, distributed, disaggregated) |
|
MI30x DeepSeek-R1-0528 configs (single, distributed, disaggregated) |
|
MI30x Qwen3.5-397B configs (single, distributed, disaggregated) |
|
MI35x Llama 3.1 70B configs (single, distributed, disaggregated) |
|
MI35x DeepSeek-R1-0528 configs (single, distributed, disaggregated) |
|
MI35x Qwen3.5-397B configs (single, distributed, disaggregated) |
Threshold keys use the form ISL=<n>,OSL=<n>,TP=<n>,PP=<n>,CONC=<n>. Each value is a
metric map (for example output_throughput_per_sec, mean_ttft_ms, mean_tpot_ms,
goodput, mfu) with kind and value fields. Accuracy cells use
BENCH=lm_eval_hellaswag and BENCH=lm_eval_gsm8k.
File structure#
Shipped templates use these top-level keys:
The following table describes each top-level key in a SGLang configuration file.
Key |
Description |
|---|---|
|
When |
|
Filename of the threshold JSON in the same directory. |
|
|
|
Image, name, lifetime, and |
|
Cluster GPU label. Shipped values contain |
|
Model, TP/PP, |
|
|
|
Disaggregated only. Needle-in-a-haystack probe settings. The stage runs only when
|
|
|
|
Optional per-combo overrides (for example |
|
|
The HuggingFace or filesystem path loaded by SGLang is server_params.model.
Example: single-node template#
mi3xx_sglang_llama_70b_single.json (abbreviated)
{
"enforce_thresholds": false,
"threshold_json": "mi325_sglang_llama_70b_threshold.json",
"paths": {
"shared_fs": "/home/{user-id}",
"models_dir": "/root/models",
"log_dir": "{shared_fs}/LOGS/sglang",
"hf_token_file": "{shared_fs}/.hf_token"
},
"container": {
"lifetime": "per_run",
"name": "sglang_container",
"image": "<changeme>",
"runtime": {
"name": "docker",
"args": {
"network": "host",
"ipc": "host",
"privileged": true,
"shm_size": "128G",
"volumes": [
"/home/{user-id}:/home/{user-id}",
"/mnt/dtni/models:/root/models"
],
"devices": [ "/dev/dri", "/dev/kfd" ],
"env": {
"NCCL_DEBUG": "ERROR",
"SGLANG_USE_AITER": "1"
}
}
}
},
"server_params": {
"backend": "sglang",
"nnodes": "1",
"model": "meta-llama/Llama-3.1-70B-Instruct",
"tensor_parallelism": "8",
"pipeline_parallelism": "1",
"add_flags": [ "--attention-backend aiter" ]
},
"benchmark_params": {
"backend": "sglang",
"data_set_name": "random",
"num_prompts": "25",
"model_num_params": "70000000000",
"peak_gpu_tflops": "2615"
},
"accuracy": { "tasks": [ { "id": "lm_eval_hellaswag" }, { "id": "lm_eval_gsm8k" } ] },
"sweep": {
"runs": [
{ "combo": "ISL=1024,OSL=1024,TP=8,PP=1,CONC=64" }
]
}
}
General config parameters#
General configuration parameters and their descriptions are listed below.
Parameter |
Example |
Description |
|---|---|---|
|
|
Docker image with SGLang and ROCm. Use an MI30x image for |
|
|
Container instance name on each participating node. |
|
|
How many hosts to take from |
|
|
Client HTTP port. Omitted configs use |
|
|
HuggingFace token file for model download. |
|
|
Docker shared memory size. |
|
|
Shared log root. |
|
|
SGLang server log level. |
|
|
Exported into every server, proxy, and benchmark script. Blank or omitted values become |
|
|
NCCL log level (multi-node). |
|
|
GPU devices passed into the container. MI30x multi-node configs also include |
|
list of |
Bind mounts for home, models, and (multi-node) RDMA libraries. See Volume mounts. |
Single-node only (sglang_single)#
The suite uses the first host in cluster.json and ignores the rest. Leave benchmark_serv_node unset. server_params.nnodes must be 1.
Unified multi-node (sglang_distributed)#
The suite takes the first nnodes hosts from cluster.json. Rank 0 is the dist-init master and the benchmark node. Leave server_node_list and benchmark_serv_node unset. If nnodes is larger than the cluster, the suite fails at load time.
Parameter |
Description |
|---|---|
|
Distributed init port on rank 0. Omitted configs use |
|
NCCL InfiniBand/RoCE device list and GID index ( |
|
Ethernet interfaces for socket/Gloo fallback. |
Other scalar |
Copied into the in-container |
Disaggregated prefill-decode (sglang_disagg_distributed)#
Uses the multi-node network env fields above. The loader drops any configured role lists and assigns them from the first nnodes hosts:
Rank 0 is the prefill coordinator, the proxy router, and the benchmark node.
Rank 1 is the decode coordinator.
The remaining hosts split in half: the first half joins prefill, the second half joins decode.
nnodes must be even and at least 2, and the cluster must contain at least that many hosts.
Parameter |
Description |
|---|---|
|
Proxy client port. Omitted configs use |
|
Internal service ports. Omitted configs use |
|
Coordinator ports. Omitted configs use |
|
PD routing policy. Shipped templates use |
|
Mooncake Transfer Engine settings on MI35x disaggregated templates ( |
|
When true, |
benchmark_params / model settings#
Benchmark and model configuration parameters are listed below.
Inference tests#
benchmark_paramsRandom synthetic load using
sglang.bench_serving. ISL/OSL/concurrency cells come fromsweep.runs(or every performance cell in the threshold file ifrunsis empty).input_lengthandoutput_lengthare injected at collection time.Combo keys match a threshold cell by the full
ISL,OSL,TP,PP,CONCkey. If that key is missing and exactly one threshold cell has the sameISL,OSL,CONC, that cell is used. TP and PP on the combo then only label the run; they do not relaunch the server. If several threshold cells share that shape, collection fails and the combo must use the exact key.sweepssupplies per-combo overrides such asnum_prompts. Emptyrunsselects each performance cell in the threshold JSON once.enforce_thresholds(top-level): whenfalse, measured throughput/latency is recorded and reported but does not fail the run. Whentrue, results are compared against the matched threshold cell.num_prompts,random_range_ratio,model_num_params,peak_gpu_tflops: bench workload and MFU calculation inputs.
accuracy.tasks(lm_eval_hellaswag,lm_eval_gsm8k)Accuracy tasks using lm-eval. Thresholds for accuracy metrics are always enforced when configured in the threshold file.
Volume mounts#
Single-node configs mount only user home and model storage.
MI30x distributed and disaggregated configs add Thor/Broadcom RDMA mounts:
{
"volumes": [
"/dev/infiniband:/dev/infiniband",
"/usr/local/lib/libbnxt_re-rdmav34.so:/usr/lib/x86_64-linux-gnu/libibverbs/libbnxt_re-rdmav34.so:ro",
"/usr/lib/x86_64-linux-gnu/libibverbs.so.1:/usr/lib/x86_64-linux-gnu/libibverbs.so.1:ro",
"/lib/libibverbs.d:/lib/libibverbs.d"
]
}
MI35x distributed and disaggregated configs mount the Ionic RDMA libraries instead:
{
"volumes": [
"/dev/infiniband:/dev/infiniband",
"/usr/lib/x86_64-linux-gnu/libionic.so.1:/usr/lib/x86_64-linux-gnu/libionic.so.1:ro",
"/usr/lib/x86_64-linux-gnu/libionic.so:/usr/lib/x86_64-linux-gnu/libionic.so:ro",
"/usr/lib/x86_64-linux-gnu/libibverbs/libionic-rdmav34.so:/usr/lib/x86_64-linux-gnu/libibverbs/libionic-rdmav34.so:ro",
"/etc/libibverbs.d/ionic.driver:/etc/libibverbs.d/ionic.driver:ro"
]
}
Those host paths must exist on every participating node. Change the mount source if the library lives somewhere else.
Disaggregated architecture overview#
SGLang disaggregated prefill-decode separates inference into:
Prefill nodes — process prompts and build KV cache.
Decode nodes — autoregressive token generation from cached KV states.
Proxy router — routes requests between prefill and decode clusters.
Use sglang_disagg_distributed with a *_disaggregated.json template. Unified
multi-node serving (no PD split) uses sglang_distributed. The node split follows
cluster.json order and server_params.nnodes, as described above.
Performance metrics#
The results table and threshold files use:
Output throughput (
output_throughput_per_sec) — output tokens per second.TTFT (
mean_ttft_ms) — mean time to first token.TPOT (
mean_tpot_ms) — mean time per output token.E2E latency (
mean_e2e_latency_ms) — end-to-end request latency.Goodput — fraction of successful requests.
MFU — model FLOPs utilization derived from
model_num_paramsandpeak_gpu_tflops.
Troubleshooting#
- Container launch
Verify
container.imageon all nodes,devicesGPU paths, andshm_size. Single-node runs need only/dev/driand/dev/kfd. Put ROCm/SGLang knobs as scalarenvkeys (SGLANG_USE_AITER,GPU_ARCHS,HSA_FORCE_FINE_GRAIN_PCIE, …). Ifpaths.log_diris owned by root and passwordless sudo is unavailable, the cleanuprmfails.- Multi-node networking
Match
NCCL_IB_HCAto the fabric. MI30x templates expect Broadcombnxtmounts andrdma*devices. MI35x templates expect Ionic library mounts andionic_*devices. Confirm the mounted.sofiles exist on the host before the container starts.- Host selection
Put hosts in
cluster.jsonin rank order. The config does not choose nodes.sglang_singleuses the first host.sglang_distributeduses the firstnnodeshosts.sglang_disagg_distributedneeds an evennnodesand splits that prefix into equal prefill and decode groups.- Sweep collection
Each listed
sweep.runscombo must match a threshold cell by the full key, or be the only cell with thatISL,OSL,CONC. Emptyrunsselects every performance cell in the threshold JSON once. A sweep TP/PP that differs from the server TP/PP does not relaunch the server.- Model access
Set
paths.hf_token_filefor HuggingFace models or mount local weights under/root/modelsusingvolumes.