|
| 1 | +# Kimi K2.5 MI355X vLLM Disagg Notes |
| 2 | + |
| 3 | +## Goal |
| 4 | + |
| 5 | +Make `kimik2.5-fp4-mi355x-vllm-disagg` beat the single-node MI355X baseline on per-GPU throughput, not just total throughput from using more GPUs. |
| 6 | + |
| 7 | +## B200/GB200 Recipe Lessons |
| 8 | + |
| 9 | +The B200/GB200 Kimi disagg recipes use worker counts, not 8 GPUs per `P` or `D`. |
| 10 | + |
| 11 | +Examples from `benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k2.5-fp4`: |
| 12 | + |
| 13 | +- `1p1d dep4-dep8`: prefill = 1 worker x 4 GPUs, decode = 1 worker x 8 GPUs. |
| 14 | +- `1p1d dep4-dep16`: prefill = 1 worker x 4 GPUs, decode = 1 worker x 16 GPUs. |
| 15 | +- `3p1d dep4-dep16`: prefill = 3 workers x 4 GPUs, decode = 1 worker x 16 GPUs. |
| 16 | +- `6p1d dep4-dep16`: prefill = 6 workers x 4 GPUs, decode = 1 worker x 16 GPUs. |
| 17 | +- `8p1d dep4-dep16`: prefill = 8 workers x 4 GPUs, decode = 1 worker x 16 GPUs. |
| 18 | + |
| 19 | +So the useful lesson is the P:D GPU ratio and worker granularity, not the literal P/D count. |
| 20 | + |
| 21 | +## MI355X Mapping |
| 22 | + |
| 23 | +Current MI355X launcher appears node-granular: one worker normally consumes an 8-GPU MI355X node. |
| 24 | + |
| 25 | +The first sub-node slicing layer is implemented by role-local visibility in |
| 26 | +`benchmarks/multi_node/amd_utils/job.slurm`: prefill workers see |
| 27 | +`0..PREFILL_TP_SIZE-1`, decode workers see `0..DECODE_TP_SIZE-1`. Benchmark |
| 28 | +accounting in `server_vllm.sh` now reports `PREFILL_TP_SIZE * xP` and |
| 29 | +`DECODE_TP_SIZE * yD`, instead of assuming 8 GPUs per worker. |
| 30 | + |
| 31 | +With sub-node GPU slicing via `HIP_VISIBLE_DEVICES` / `ROCR_VISIBLE_DEVICES`, |
| 32 | +the closest mapping is: |
| 33 | + |
| 34 | +- GB200 `dep4` prefill -> MI355X 4-GPU prefill worker. |
| 35 | +- GB200 `dep8` decode -> MI355X 8-GPU decode worker. |
| 36 | +- GB200 `dep16` decode -> MI355X 2 x 8-GPU decode workers. |
| 37 | + |
| 38 | +If sub-node slicing is not available, use coarser whole-node sweeps: |
| 39 | + |
| 40 | +- `1k1k`: start with `1P1D` and `1P2D`; this is decode-heavy. |
| 41 | +- `8k1k`: start with `2P1D`, then possibly `3P1D`; this is prefill-heavy. |
| 42 | + |
| 43 | +Avoid interpreting `3P1D` as a universally good first target on MI355X. With whole nodes, `3P1D` already means 32 GPUs. |
| 44 | + |
| 45 | +## DP-Attention / FP8 KV Finding |
| 46 | + |
| 47 | +Validated on `mia1-p01-g07`: |
| 48 | + |
| 49 | +- `DP8 + EP8 + TP1 + --kv-cache-dtype fp8` fails. |
| 50 | +- The failure enters AITER MLA `mla_a8w8_qh64_qseqlen1_gqaratio64_v3_ps`. |
| 51 | +- With vLLM persistent metadata enabled, this hits GPU memory access faults. |
| 52 | +- With persistent metadata disabled via a local monkey patch, AITER reports: |
| 53 | + `fp8/fp8 with gqa_ratio=64 only supports decode_qlen=1 in persistent mode`. |
| 54 | +- `DP8 + EP8 + TP1` with auto/bf16 KV starts successfully and returns a chat completion. |
| 55 | + |
| 56 | +Therefore the current safe recipe rule is: |
| 57 | + |
| 58 | +- DP-attn can be used for high-concurrency sweeps. |
| 59 | +- DP-attn must strip `--kv-cache-dtype fp8` and run auto/bf16 KV on MI355X. |
| 60 | +- Keep TP8 + fp8 KV as a separate non-DP baseline for low/mid concurrency. |
| 61 | + |
| 62 | +## Candidate MI355X Recipe Sweep |
| 63 | + |
| 64 | +Minimal first pass before DP-attn is stable: |
| 65 | + |
| 66 | +1. `1k1k`, `1P1D`, prefill TP4/EP4 + decode TP8/EP8, conc `[512, 1024]`. |
| 67 | +2. `1k1k`, `1P2D`, prefill TP4/EP4 + decode TP8/EP8, conc `[1024, 2048]`. |
| 68 | +3. `8k1k`, `2P1D`, prefill TP4/EP4 + decode TP8/EP8, conc `[512, 1024]`. |
| 69 | +4. `8k1k`, `4P1D`, prefill TP4/EP4 + decode TP8/EP8, conc `[1024]`. |
| 70 | + |
| 71 | +The corresponding normal-only CI config is |
| 72 | +`.github/configs/amd-kimi-mi355x-mpnd-normal.yaml`. It excludes DP-attn rows so |
| 73 | +the run can finish green and produce ingestible artifacts. |
| 74 | + |
| 75 | +Older whole-node fallback if sub-node slicing regresses: |
| 76 | + |
| 77 | +1. `1k1k`, `1P1D`, TP8 prefill + TP8/EP8 decode, conc `[512, 1024]`. |
| 78 | +2. `1k1k`, `1P2D`, TP8 prefill + TP8/EP8 decode, conc `[1024, 2048]`. |
| 79 | +3. `1k1k`, `1P2D`, DP8/EP8 auto-KV, conc `[1024, 2048]`. |
| 80 | +4. `8k1k`, `2P1D`, TP8 prefill + TP8/EP8 decode, conc `[512, 1024]`. |
| 81 | +5. `8k1k`, `3P1D`, TP8 prefill + TP8/EP8 decode, conc `[1024, 2048]`. |
| 82 | +6. `8k1k`, `3P1D`, DP8/EP8 auto-KV, conc `[1024, 2048]`. |
| 83 | + |
| 84 | +If sub-node 4-GPU workers become available, prefer matching GB200 ratios more directly: |
| 85 | + |
| 86 | +- `1k1k`: P=4G, D=8G or 16G. |
| 87 | +- `8k1k`: P=3x4G, D=16G. |
| 88 | + |
| 89 | +## CI Run 29200792444 / 29200792335 Diagnosis |
| 90 | + |
| 91 | +Both pinned Kimi K2.5 MPND runs produced successful normal heterogeneous TP |
| 92 | +jobs and failed only on DP-attn comparison jobs. |
| 93 | + |
| 94 | +Normal successful rows were present in artifacts: |
| 95 | + |
| 96 | +- `29200792444` (`1k1k`): TP4/EP4 prefill + TP8/EP8 decode succeeded for |
| 97 | + `1P1D` and `1P2D`. |
| 98 | +- `29200792335` (`8k1k`): TP4/EP4 prefill + TP8/EP8 decode succeeded for |
| 99 | + `2P1D` and `4P1D`. |
| 100 | + |
| 101 | +The missing 1k1k rows in the unofficial UI were not because artifacts were |
| 102 | +absent. `collect-results` succeeded, but the whole workflow conclusion was |
| 103 | +failure because DP-attn jobs failed, so the unofficial ingestion path likely |
| 104 | +ignored or did not refresh partial data from the failed run. |
| 105 | + |
| 106 | +The old artifacts also show why the first-layer accounting fix is needed: |
| 107 | + |
| 108 | +- `1k1k 1P1D` reported `num_prefill_gpu=8`, but should be 4 for TP4. |
| 109 | +- `1k1k 1P2D` reported `num_prefill_gpu=8`, but should be 4 for TP4. |
| 110 | +- `8k1k 2P1D` reported `num_prefill_gpu=16`, but should be 8 for 2 x TP4. |
| 111 | +- `8k1k 4P1D` reported `num_prefill_gpu=32`, but should be 16 for 4 x TP4. |
| 112 | + |
| 113 | +DP-attn failures in those runs are separate from heterogeneous TP. Server and |
| 114 | +MoRI proxy readiness completed, but benchmark reported: |
| 115 | + |
| 116 | +```text |
| 117 | +Successful requests: 0 |
| 118 | +FAIL: request failure rate 100.0% exceeds 5% threshold (0/10240 completed) |
| 119 | +``` |
| 120 | + |
| 121 | +Those failed jobs did not preserve per-request error bodies, so the next DP-attn |
| 122 | +debug pass should enable detailed benchmark output or server access/error logs. |
| 123 | + |
| 124 | +## B200 Parameters Worth Adapting |
| 125 | + |
| 126 | +Applicable ideas: |
| 127 | + |
| 128 | +- Prefill: `enforce-eager=true`. |
| 129 | +- Decode: `FULL_DECODE_ONLY` cudagraph. |
| 130 | +- Disable prefix caching for fixed-seq throughput sweeps. |
| 131 | +- Consider disabling chunked prefill for fixed-seq disagg. |
| 132 | +- Split `max-num-seqs` by workload: |
| 133 | + - `1k1k`: larger, e.g. 512/1024. |
| 134 | + - `8k1k` prefill: smaller, e.g. 64/128. |
| 135 | + - `8k1k` decode: 256/512. |
| 136 | +- Tune decode `max-cudagraph-capture-size` separately, starting with 256/512. |
| 137 | +- Increase frontend/router capacity at high concurrency. B200/GB200 recipes often enable multiple frontends for high-throughput cases. |
| 138 | + |
| 139 | +Not directly portable to MI355X: |
| 140 | + |
| 141 | +- `FLASHINFER_MLA`. |
| 142 | +- `flashinfer_nvlink_one_sided`. |
| 143 | +- NVIDIA NCCL MNNVL/NVLS knobs. |
| 144 | +- NIXL connector assumptions. |
| 145 | + |
| 146 | +## CI Run 28083945960 Diagnosis |
| 147 | + |
| 148 | +Run: `https://github.com/SemiAnalysisAI/InferenceX/actions/runs/28083945960` |
| 149 | + |
| 150 | +Job `83145269559` is `multi-node eval`, not benchmark: |
| 151 | + |
| 152 | +- The job name includes `eval-only`. |
| 153 | +- `benchmark-multinode-tmpl.yml` skips benchmark result checks when `inputs.eval-only == true`. |
| 154 | +- The log says `EVAL_ONLY mode: skipping throughput benchmark`. |
| 155 | +- It then runs `lm_eval` on `utils/evals/gsm8k.yaml`. |
| 156 | +- The job passed GSM8K: |
| 157 | + - strict match: `0.9310` |
| 158 | + - flexible extract: `0.9431` |
| 159 | + |
| 160 | +Most failed jobs did not run servers or benchmarks. They failed during `actions/checkout` cleanup: |
| 161 | + |
| 162 | +```text |
| 163 | +File was unable to be removed Error: EACCES: permission denied, |
| 164 | +rmdir '/it-share/gharunners2/gharunner06/actions-runner/_work/InferenceX/InferenceX/LOGS/agentic' |
| 165 | +``` |
| 166 | + |
| 167 | +This is a stale root-owned or otherwise non-runner-owned workspace artifact. Because checkout runs before repo code is available, the existing pre/post launch cleanup in runner scripts cannot fix this class of failure. |
| 168 | + |
| 169 | +The successful eval job also shows workspace permission problems after Slurm completion: |
| 170 | + |
| 171 | +```text |
| 172 | +cp .../eval_results/meta_env.json .../InferenceX/meta_env.json: Permission denied |
| 173 | +cp .../eval_results/results_*.json .../InferenceX/results_*.json: Permission denied |
| 174 | +cp .../eval_results/samples_gsm8k_*.jsonl .../InferenceX/samples_*.jsonl: Permission denied |
| 175 | +``` |
| 176 | + |
| 177 | +Despite those errors, the result file was present later and upload/verification passed. The copy loop is misleading because it echoes "Copied" even when `cp` failed. |
| 178 | + |
| 179 | +## CI Cleanup Recommendations |
| 180 | + |
| 181 | +1. Add a pre-checkout cleanup step in the reusable workflow before `actions/checkout`, using `sudo rm -rf` for known stale paths: |
| 182 | + - `$GITHUB_WORKSPACE/LOGS` |
| 183 | + - `$GITHUB_WORKSPACE/benchmark_logs` |
| 184 | + - `$GITHUB_WORKSPACE/benchmark_artifacts` |
| 185 | + - root-level `results*.json`, `samples*.jsonl`, `meta_env.json` |
| 186 | + |
| 187 | +2. Fix eval artifact extraction in `runners/launch_mi355x-amds.sh`: |
| 188 | + - Check `cp` return codes before printing "Copied". |
| 189 | + - If copying into `$GITHUB_WORKSPACE` needs sudo due to ownership drift, use `sudo cp` followed by `sudo chown "$USER":"$USER"` on copied artifacts. |
| 190 | + |
| 191 | +3. Ensure containers do not write under `$GITHUB_WORKSPACE` as root where possible. Prefer `benchmark_logs` and then copy artifacts back with normalized ownership. |
| 192 | + |
| 193 | +4. Manual immediate cleanup for current runners: |
| 194 | + - On affected runner hosts, remove stale workspace paths with sudo: |
| 195 | + `sudo rm -rf /it-share/gharunners2/gharunner*/actions-runner/_work/InferenceX/InferenceX/LOGS` |
0 commit comments