xDiT inference benchmark configuration for Cluster Validation Suite (CVS)#
2026-10-27
13 min read time
CVS ships five xDiT suites under cvs/tests/inference/xdit/. Each suite reads a JSON
file from cvs/input/config_file/inference/xdit/. Latency and CLIPScore limits live in a
sibling threshold file named by top-level threshold_json. mi3xx_* and mi35x_*
templates for the same workload share that file.
Single-node templates run one independent docker+torchrun job on every node in the cluster file.
Distributed templates run one coordinated torchrun job (
server_params.nnodes >= 2). Replace every<changeme>(image, model, GPU label, and NCCL/network fields) before running. Stage model weights on every participating node before the run.
See Run xDiT diffusion inference tests with Cluster Validation Suite (CVS) for more information on running these tests.
Note
{user-id}and{home}in path strings are resolved at runtime.{paths.<name>}references expand from thepathsblock.server_params.modelis a Hugging Face repo id or an absolute host path. A repo id requires a pre-populated cache underpaths.models_dir. An absolute path is bind-mounted into the container.FLUX.1-dev and FLUX.2-dev share
xdit_flux_dev_*; pick the matching JSON.A relative
threshold_jsonis loaded from the config’s directory. If that copy is missing, CVS falls back to the packaged file of the same name.
Configuration files#
Each xDiT suite maps to a mi3xx template and a mi35x template. mi3xx covers
MI300X, MI325X, and MI350X (gpu_name hint mi325). mi35x covers MI355X
(gpu_name hint mi355) and ships Ionic RDMA settings on distributed configs.
Config file |
Threshold file |
Use with suite |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copy a template and its threshold file:
cvs config list inference/xdit
cvs config copy inference/xdit/mi3xx_xdit_flux1_dev_single.json \
--output ~/cvs_workspace/inference/xdit/mi3xx_xdit_flux1_dev_single.json
cvs config copy inference/xdit/xdit_flux1_dev_single_threshold.json \
--output ~/cvs_workspace/inference/xdit/xdit_flux1_dev_single_threshold.json
File structure#
Packaged templates use these top-level keys:
Key |
Description |
|---|---|
|
Label shipped as |
|
When |
|
Sibling threshold filename. Both GPU families for a workload share one file. |
|
|
|
Image, name, and |
|
|
|
Flat FLUX or WAN workload fields. The loader wraps them as |
Example: FLUX.1-dev single-node#
mi3xx_xdit_flux1_dev_single.json (abbreviated)
{
"_comment": "FLUX.1-dev single-node xDiT workload. Thresholds live in the sibling threshold JSON. Download model before run.",
"gpu_name": "<changeme> mi325",
"enforce_thresholds": true,
"threshold_json": "xdit_flux1_dev_single_threshold.json",
"paths": {
"shared_fs": "{home}",
"models_dir": "{home}/.cache/huggingface",
"log_dir": "{home}/cvs_flux1_output",
"hf_token_file": "{home}/.hf_token"
},
"container": {
"lifetime": "per_run",
"name": "flux-benchmark_single",
"_image_example": "amdsiloai/pytorch-xdit:v25.11.2",
"image": "<changeme>",
"runtime": {
"name": "docker",
"args": {
"network": "host",
"ipc": "host",
"privileged": true,
"shm_size": "128G",
"volumes": [
"{paths.models_dir}:/hf_home",
"{paths.log_dir}:/outputs"
],
"devices": ["/dev/dri", "/dev/kfd"],
"env": { "NCCL_DEBUG": "ERROR" }
}
}
},
"server_params": {
"backend": "xdit",
"nnodes": "1",
"log_level": "info",
"model": "<changeme>"
},
"benchmark_params": {
"prompt": "A small cat",
"seed": 42,
"num_inference_steps": 25,
"num_repetitions": 25,
"height": 1024,
"width": 1024,
"ulysses_degree": 8,
"ring_degree": 1,
"use_torch_compile": true,
"torchrun_nproc": 8
}
}
mi35x_xdit_flux1_dev_single.json matches that layout with gpu_name hint mi355,
an example model path, and use_torch_compile set to false.
General parameters#
The following parameters appear in every packaged xDiT template.
Parameter |
Example |
Description |
|---|---|---|
|
|
Image to run. |
|
|
Docker name. |
|
|
Host token file for gated models. The token is read on the runner and is not passed as |
|
|
Host HF cache or model root, mounted at |
|
|
Host directory for |
|
HF id or |
Repo id (offline cache) or absolute host path. Stage the weights before the run. |
|
|
Participating node count. Distributed templates require |
|
|
GPU device nodes. Distributed templates also pass InfiniBand devices. |
|
|
Bind mounts. FLUX.2 adds |
|
|
Extra environment variables inside the container. |
Distributed network fields#
Distributed templates put NCCL and socket settings in container.runtime.args.env.
CVS copies NCCL_IB_HCA, NCCL_IB_GID_INDEX, NCCL_SOCKET_IFNAME,
GLOO_SOCKET_IFNAME, GLOO_TCP_IFNAME, and NCCL_DEBUG into the launch environment.
Template family |
Shipped example |
|---|---|
|
Broadcom bnxt library mounts, |
|
Ionic userspace library mounts, |
Replace every <changeme> value, and edit the Ionic or bnxt library paths when the host
build differs. master_addr is optional; an empty value probes the first address on
the rank-0 node. master_port defaults to 29500.
benchmark_params for FLUX#
Used by all FLUX templates. The loader stores the flat object as flux1_dev_t2i.
FLUX.2 sets model_type to flux2, which selects flux2_example.py. FLUX.1 uses
run_usp.py. A repo or path containing flux2 or flux.2 is also treated as FLUX.2
when model_type is omitted.
Parameter |
Example |
Description |
|---|---|---|
|
|
FLUX.2 only. |
|
|
Generation prompt and RNG seed. |
|
|
FLUX.2 guidance. FLUX.1 templates omit this. |
|
|
Denoising steps (FLUX.1 |
|
|
Text encoder sequence length. |
|
|
Disable resolution binning. |
|
|
Warmup, then measured repetitions. FLUX.1 parse requires |
|
|
Output image size. |
|
|
Sequence-parallel layout. Product with pipefusion, tensor-parallel, and data-parallel degrees (each default |
|
|
|
|
|
Processes (GPUs) per node. |
|
omitted |
CLIP checkpoint id or local path used to score saved PNGs. Defaults to |
benchmark_params for WAN 2.2#
The loader stores the flat object as wan22_i2v_a14b.
Native WAN (*_xdit_wan22_14b_single.json)#
Runs /app/Wan2.2/run.py. The threshold metric is max_avg_total_time_s.
num_benchmark_steps is required, and the output must contain that many rank0_step*.json files.
Parameter |
Example |
Description |
|---|---|---|
|
(long I2V prompt) |
Image-to-video prompt. |
|
|
Frame size. |
|
|
Number of video frames. |
|
|
Measured steps after compile/warmup. Parse requires this count of rank-0 step JSONs. |
|
|
|
|
|
GPUs per node. Single-node native WAN infers ulysses from this value and ring |
|
omitted |
CLIP checkpoint id or local path for sampled-frame scoring. Defaults to |
|
omitted |
Frames sampled evenly from the MP4, including the first and last. Defaults to |
Diffusers xFuser WAN#
*_xdit_wan22_14b_diffusers_*.json additionally set:
Parameter |
Description |
|---|---|
|
|
|
|
|
In-container path to |
|
Generate an in-container input image when true. |
|
Install video encode deps inside the container when true. |
|
|
|
Fail parse if |
|
Denoising and warmup (packaged templates: |
|
Packaged Diffusers templates set |
|
Packaged Diffusers templates set |
|
Parallel layout. Distributed: product must equal |
|
GPUs per node ( |
Volume mounts#
FLUX.1 / WAN native templates mount the model cache and the log directory:
"volumes": [
"{paths.models_dir}:/hf_home",
"{paths.log_dir}:/outputs"
]
An absolute server_params.model path that is not already mounted is bind-mounted at
/model.
FLUX.2 also mounts the in-tree example. Adjust the host path to your CVS checkout:
"{home}/cvs/cvs/lib/inference/xdit/scripts/flux2_example.py:/benchmark/flux2_example.py"
WAN Diffusers mounts the xFuser example the same way:
"{home}/cvs/cvs/lib/inference/xdit/scripts/wan_i2v_example.py:/benchmark/wan_i2v_example.py"
Distributed templates add the host home directory, /dev/infiniband, and the fabric
provider libraries (bnxt on mi3xx, Ionic on mi35x).
Threshold files#
Each threshold file is a map of GPU key to latency and CLIPScore limits. Keys that start with _
are comments and are ignored. Allowed specific keys are mi300x, mi325, mi350,
and mi355. auto is the fallback when the detected key is absent.
GPU type comes from the PCI device ID under /sys/class/drm/card*/device/device.
0x74a5 selects mi325, 0x75a0 selects mi350, and 0x75a3 selects
mi355. Any other ID, including MI300X, selects mi300x. A threshold key applies
only when detection reports that same string.
Threshold file |
Metric |
Shipped keys (seconds) |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A detected GPU with no key of its own uses auto. MI350X on
xdit_flux1_dev_distributed_threshold.json has no mi350 entry, so it uses the
auto limit of 12 seconds. MI325X (0x74a5) uses the mi325 limit when that key
exists, and auto otherwise.
Comments in the FLUX.1 single and native WAN files mark which latency keys were measured and
which remain placeholders. Tune the file for your stack before production gating.
Set enforce_thresholds to false to record latency and CLIPScore without failing those stages.
Every shipped GPU entry also sets a CLIPScore floor of 20.0. That floor is an unvalidated
starting value, not a measured pass bar.
Workload |
Threshold key |
What is scored |
|---|---|---|
FLUX.1 and FLUX.2 |
|
Mean CLIPScore of saved PNGs ( |
WAN native |
|
Mean CLIPScore of frames sampled from |
WAN Diffusers |
|
Mean CLIPScore of frames sampled from |
test_clip_score runs inside the benchmark container and writes results/clipscore.json.
The controller reads that file from the node, so the output directory does not have to be on
NFS. Prompts longer than the CLIP text context are truncated, and the JSON sets
prompt_truncated when that happens. The default WAN prompt is longer than 77 tokens.
Performance metrics#
FLUX — average
pipe_timevsmax_avg_pipe_time_s; artifactsresults/timing.jsonandflux_*.png. FLUX.1 also requiresnum_repetitionssamples. CLIPScore of those PNGs vsmin_clip_score, written toresults/clipscore.json.WAN native — average
total_timevsmax_avg_total_time_s;rank0_step*.json(count must equalnum_benchmark_steps) andvideo.mp4. Mean frame CLIPScore of that MP4 vsmin_mean_frame_clip_score.WAN Diffusers — average pipe/epoch time vs
max_avg_pipe_time_s;results/timing.jsonandresults/video_i2v.mp4. Mean frame CLIPScore of that MP4 vsmin_mean_frame_clip_score.
Troubleshooting#
- ``/dev/kfd not found``
Run on GPU compute nodes, not login nodes.
test_verify_prerequisitesalso requirestorchruninside the container.- Container image not found locally
docker pullthe configuredcontainer.imageon every execution node. The image field must replace<changeme>.- Local model path not found
Stage weights on every participating node before the run. A Hugging Face repo id must already exist under
paths.models_dir. An absoluteserver_params.modelpath must exist on each participating host.- Parallel degree product != world_size
Align
ulysses/ring(and FLUX pipefusion, tensor-parallel, and data-parallel degrees) withnnodes × torchrun_nproc.- FLUX.1 requires num_repetitions / native WAN requires num_benchmark_steps
Set the field in
benchmark_params. The parsed artifact count must match that value.- Missing ``timing.json`` / ``video.mp4``
The benchmark docker exit code was non-zero or artifacts were written elsewhere; inspect the log tail on the failing node.
- CLIP score command failed
test_verify_modelchecks thatclip_model(defaultopenai/clip-vit-base-patch32) is already in the container HF cache, or is a local directory, before the benchmark starts. The suite never downloads it. A missing checkpoint fails that stage on every host even whenenforce_thresholdsisfalse; that flag only skips the numeric floor. WAN also needs a video decoder:imageiowithimageio-ffmpeg, OpenCV, ortorchvision. xFuser installsimageiowhenwan_xfuser_install_video_depsis true. A prompt longer than 77 tokens is truncated and does not fail the stage.- CLIPScore below the shipped floor
min_clip_scoreandmin_mean_frame_clip_scoreship as20.0, which is not a measured bar. Replace the floor after a known-good run, or setenforce_thresholdstofalseto record the score.- MI355X threshold uses ``auto`` or ``mi300x``
Confirm the node exposes device ID
0x75a3. A missingmi355key in the threshold file selectsauto. An unrecognized device ID selectsmi300x. MI325X is0x74a5and selectsmi325.