|
| 1 | +# KV Transfer Connector Benchmark |
| 2 | + |
| 3 | +Unified benchmark for measuring raw transport-level performance of vLLM KV transfer connectors (NIXL, Mooncake, and FlagCX). |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +This benchmark measures bandwidth and latency for KV cache transfers using the same underlying transport libraries as vLLM's KV connectors, without the full vLLM scheduler/worker overhead. |
| 8 | + |
| 9 | +**Supported Connectors:** |
| 10 | +- **NIXL** with UCX or FLAGCX backends |
| 11 | +- **Mooncake** TransferEngine (RDMA) |
| 12 | +- **FlagCX** direct one-sided RDMA (no NIXL abstraction layer) |
| 13 | + |
| 14 | +## Requirements |
| 15 | + |
| 16 | +### NIXL |
| 17 | +```bash |
| 18 | +pip install nixl-cu12 # or nixl-cu11 depending on CUDA version |
| 19 | +pip install pyzmq torch |
| 20 | +``` |
| 21 | + |
| 22 | +For FLAGCX backend: |
| 23 | +```bash |
| 24 | +# Ensure libflagcx.so is in LD_LIBRARY_PATH |
| 25 | +# Ensure libplugin_FLAGCX.so is on the NIXL plugin path |
| 26 | +``` |
| 27 | + |
| 28 | +### Mooncake |
| 29 | +```bash |
| 30 | +# Install Mooncake following: |
| 31 | +# https://github.com/kvcache-ai/Mooncake/blob/main/doc/en/build.md |
| 32 | +pip install pyzmq torch |
| 33 | +``` |
| 34 | + |
| 35 | +### FlagCX (Direct) |
| 36 | +```bash |
| 37 | +# Build FlagCX from source: |
| 38 | +# https://github.com/FlagOpen/FlagCX |
| 39 | +# Set FLAGCX_PATH to the FlagCX root directory |
| 40 | +export FLAGCX_PATH=/path/to/FlagCX |
| 41 | +pip install pyzmq torch |
| 42 | +``` |
| 43 | + |
| 44 | +## Usage |
| 45 | + |
| 46 | +### NIXL with UCX Backend |
| 47 | + |
| 48 | +**Server (Node A, IP: 10.8.2.169):** |
| 49 | +```bash |
| 50 | +python kv_transfer_benchmark.py --connector=nixl --role=server \ |
| 51 | + --remote-ip=10.8.2.169 --device=gpu --nixl-backend=UCX \ |
| 52 | + --sizes=1048576,16777216,104857600 --iters=20 |
| 53 | +``` |
| 54 | + |
| 55 | +**Client (Node B):** |
| 56 | +```bash |
| 57 | +python kv_transfer_benchmark.py --connector=nixl --role=client \ |
| 58 | + --remote-ip=10.8.2.169 --device=gpu --nixl-backend=UCX \ |
| 59 | + --sizes=1048576,16777216,104857600 --iters=20 |
| 60 | +``` |
| 61 | + |
| 62 | +### NIXL with FLAGCX Backend |
| 63 | + |
| 64 | +Same as above, but add `--nixl-backend=FLAGCX`: |
| 65 | +```bash |
| 66 | +python kv_transfer_benchmark.py --connector=nixl --role=server \ |
| 67 | + --remote-ip=10.8.2.169 --device=gpu --nixl-backend=FLAGCX \ |
| 68 | + --sizes=1048576,16777216,104857600 --iters=20 |
| 69 | +``` |
| 70 | + |
| 71 | +### Mooncake |
| 72 | + |
| 73 | +**Server:** |
| 74 | +```bash |
| 75 | +python kv_transfer_benchmark.py --connector=mooncake --role=server \ |
| 76 | + --remote-ip=10.8.2.169 --device=gpu \ |
| 77 | + --sizes=1048576,16777216,104857600 --iters=20 |
| 78 | +``` |
| 79 | + |
| 80 | +**Client:** |
| 81 | +```bash |
| 82 | +python kv_transfer_benchmark.py --connector=mooncake --role=client \ |
| 83 | + --remote-ip=10.8.2.169 --device=gpu \ |
| 84 | + --sizes=1048576,16777216,104857600 --iters=20 |
| 85 | +``` |
| 86 | + |
| 87 | +### FlagCX (Direct) |
| 88 | + |
| 89 | +**Server:** |
| 90 | +```bash |
| 91 | +cd ~/WORKDIR/FlagCX |
| 92 | +python3 test/perf/kv_transfer/kv_transfer_benchmark.py --connector=flagcx --role=server \ |
| 93 | + --remote-ip=10.8.2.169 --device=gpu \ |
| 94 | + --sizes=1048576,16777216,104857600 --iters=20 |
| 95 | +``` |
| 96 | + |
| 97 | +**Client:** |
| 98 | +```bash |
| 99 | +cd ~/WORKDIR/FlagCX |
| 100 | +python3 test/perf/kv_transfer/kv_transfer_benchmark.py --connector=flagcx --role=client \ |
| 101 | + --remote-ip=10.8.2.169 --device=gpu \ |
| 102 | + --sizes=1048576,16777216,104857600 --iters=20 |
| 103 | +``` |
| 104 | + |
| 105 | +**Custom FlagCX paths:** |
| 106 | +```bash |
| 107 | +python3 test/perf/kv_transfer/kv_transfer_benchmark.py --connector=flagcx --role=server \ |
| 108 | + --remote-ip=10.8.2.169 --device=gpu \ |
| 109 | + --flagcx-lib-path=/custom/path/libflagcx.so \ |
| 110 | + --flagcx-wrapper-path=/custom/path/FlagCX |
| 111 | +``` |
| 112 | + |
| 113 | +## Command-Line Arguments |
| 114 | + |
| 115 | +| Argument | Description | Default | |
| 116 | +|----------|-------------|---------| |
| 117 | +| `--connector` | Transport backend: `nixl`, `mooncake`, or `flagcx` | **required** | |
| 118 | +| `--role` | `server` or `client` | **required** | |
| 119 | +| `--remote-ip` | Server IP address | `0.0.0.0` | |
| 120 | +| `--device` | Memory device: `cpu` or `gpu` | `gpu` | |
| 121 | +| `--local-gpu-idx` | CUDA device index | `0` | |
| 122 | +| `--sizes` | Comma-separated transfer sizes in bytes | `1KB,4KB,16KB,64KB,256KB,1MB,4MB,16MB,64MB` | |
| 123 | +| `--iters` | Number of timed iterations per size | `100` | |
| 124 | +| `--warmup-iters` | Number of warmup iterations before timed runs | `10` | |
| 125 | +| `--num-blocks` | Number of tensor blocks per transfer | `1` | |
| 126 | +| `--nixl-backend` | NIXL backend plugin (e.g., `UCX`, `UCCL`, `FLAGCX`) | `UCX` | |
| 127 | +| `--zmq-port` | ZMQ coordination port | `9000` | |
| 128 | +| `--mooncake-protocol` | Mooncake protocol: `rdma` or `tcp` | `rdma` | |
| 129 | +| `--flagcx-lib-path` | Path to `libflagcx.so` | `$FLAGCX_PATH/build/lib/libflagcx.so` | |
| 130 | +| `--flagcx-wrapper-path` | Path to FlagCX root directory | `$FLAGCX_PATH` | |
| 131 | + |
| 132 | +**Note**: Operation type (read/write) is automatically determined by the connector: |
| 133 | +- **NIXL**: Uses `read` (client reads from server) |
| 134 | +- **Mooncake**: Uses `write` (client writes to server) |
| 135 | +- **FlagCX**: Uses `write` (client writes to server) |
| 136 | + |
| 137 | +## Output Format |
| 138 | + |
| 139 | +``` |
| 140 | +KV Transfer Benchmark connector=nixl role=client |
| 141 | + device=gpu gpu=0 op=read iters=100 warmup=10 num_blocks=1 |
| 142 | + NOTE: nixl connector uses op_type=read (matching vLLM connector semantics) |
| 143 | + nixl-backend=UCX |
| 144 | + sizes: 1.0 KB, 4.0 KB, 16.0 KB, 64.0 KB, 256.0 KB, 1.0 MB, 4.0 MB, 16.0 MB, 64.0 MB |
| 145 | +------------------------------------------------------------------------ |
| 146 | + 1.0 KB | lat= 0.123 ms | BW= 0.01 GB/s ( 0.08 Gbps) | iters=100 |
| 147 | + ✓ Verification passed |
| 148 | + 4.0 KB | lat= 0.130 ms | BW= 0.03 GB/s ( 0.25 Gbps) | iters=100 |
| 149 | + ✓ Verification passed |
| 150 | + 16.0 KB | lat= 0.145 ms | BW= 0.11 GB/s ( 0.88 Gbps) | iters=100 |
| 151 | + ✓ Verification passed |
| 152 | + 64.0 KB | lat= 0.178 ms | BW= 0.36 GB/s ( 2.88 Gbps) | iters=100 |
| 153 | + ✓ Verification passed |
| 154 | + 256.0 KB | lat= 0.234 ms | BW= 1.09 GB/s ( 8.72 Gbps) | iters=100 |
| 155 | + ✓ Verification passed |
| 156 | + 1.0 MB | lat= 0.456 ms | BW= 2.19 GB/s ( 17.52 Gbps) | iters=100 |
| 157 | + ✓ Verification passed |
| 158 | + 4.0 MB | lat= 1.234 ms | BW= 3.24 GB/s ( 25.92 Gbps) | iters=100 |
| 159 | + ✓ Verification passed |
| 160 | + 16.0 MB | lat= 4.567 ms | BW= 3.50 GB/s ( 28.00 Gbps) | iters=100 |
| 161 | + ✓ Verification passed |
| 162 | + 64.0 MB | lat= 18.234 ms | BW= 3.51 GB/s ( 28.08 Gbps) | iters=100 |
| 163 | + ✓ Verification passed |
| 164 | +------------------------------------------------------------------------ |
| 165 | +Done. |
| 166 | +``` |
| 167 | + |
| 168 | +## Verification |
| 169 | + |
| 170 | +The benchmark always performs strict element-by-element verification after each transfer size. Verification runs after timing completes, so it does not affect performance measurements. If any mismatch is detected, an `AssertionError` is raised with detailed diagnostics (which block, which element, expected vs actual value). |
| 171 | + |
| 172 | +Verification semantics per connector: |
| 173 | +- **NIXL (read)**: Client checks that received data matches server's pattern (0s) |
| 174 | +- **Mooncake (write)**: Server checks that received data matches client's pattern (1s) |
| 175 | +- **FlagCX (write)**: Server checks that received data matches client's pattern (1s) |
| 176 | + |
| 177 | +## Architecture |
| 178 | + |
| 179 | +``` |
| 180 | +┌─────────────────────────────────────────────┐ |
| 181 | +│ kv_transfer_benchmark.py │ |
| 182 | +├─────────────────────────────────────────────┤ |
| 183 | +│ TransportBenchmark (ABC) │ |
| 184 | +│ setup() → init transport, register mem │ |
| 185 | +│ run_transfer() → execute one transfer │ |
| 186 | +│ verify() → check correctness │ |
| 187 | +│ teardown() → cleanup │ |
| 188 | +├─────────────────────────────────────────────┤ |
| 189 | +│ NixlBenchmark │ |
| 190 | +│ Uses nixl_agent + nixl_agent_config │ |
| 191 | +│ ZMQ for metadata exchange │ |
| 192 | +│ Per-iteration handle init/release │ |
| 193 | +├─────────────────────────────────────────────┤ |
| 194 | +│ MooncakeBenchmark │ |
| 195 | +│ Uses mooncake.engine.TransferEngine │ |
| 196 | +│ ZMQ for session/address exchange │ |
| 197 | +│ batch_transfer_sync_write() │ |
| 198 | +├─────────────────────────────────────────────┤ |
| 199 | +│ FlagCXBenchmark │ |
| 200 | +│ Uses FLAGCXLibrary (ctypes wrapper) │ |
| 201 | +│ ZMQ for unique ID exchange │ |
| 202 | +│ flagcxPut/flagcxBatchPut + signal/wait │ |
| 203 | +└─────────────────────────────────────────────┘ |
| 204 | +``` |
| 205 | + |
| 206 | +## Comparison with vLLM Connectors |
| 207 | + |
| 208 | +This benchmark measures **transport-level** performance without vLLM overhead: |
| 209 | +- No scheduler/worker coordination |
| 210 | +- No KV cache block management |
| 211 | +- No attention metadata |
| 212 | +- Direct memory registration and transfer |
| 213 | + |
| 214 | +For **end-to-end** vLLM KV transfer benchmarks, use: |
| 215 | +```bash |
| 216 | +vllm serve <model> --kv-transfer-config '{...}' |
| 217 | +vllm bench serve --base-url http://127.0.0.1:8000 ... |
| 218 | +``` |
| 219 | + |
| 220 | +## Troubleshooting |
| 221 | + |
| 222 | +### NIXL Import Error |
| 223 | +``` |
| 224 | +Failed to import NIXL. Is the nixl Python package installed? |
| 225 | +``` |
| 226 | +**Solution**: Install NIXL with `pip install nixl-cu12` (or `nixl-cu11`) |
| 227 | + |
| 228 | +### FLAGCX Backend Not Found |
| 229 | +``` |
| 230 | +NIXL agent initialization failed |
| 231 | +``` |
| 232 | +**Solution**: Ensure `libflagcx.so` is in `LD_LIBRARY_PATH` and `libplugin_FLAGCX.so` is on the NIXL plugin path. |
| 233 | + |
| 234 | +### Mooncake Import Error |
| 235 | +``` |
| 236 | +Failed to import Mooncake. |
| 237 | +``` |
| 238 | +**Solution**: Install Mooncake following https://github.com/kvcache-ai/Mooncake/blob/main/doc/en/build.md |
| 239 | + |
| 240 | +### FlagCX Import Error |
| 241 | +``` |
| 242 | +FLAGCX_PATH not set and --flagcx-wrapper-path not provided. |
| 243 | +``` |
| 244 | +**Solution**: Set `FLAGCX_PATH` environment variable or use `--flagcx-wrapper-path` to point to the FlagCX root directory. |
| 245 | + |
| 246 | +### FlagCX Library Not Found |
| 247 | +``` |
| 248 | +OSError: libflagcx.so: cannot open shared object file |
| 249 | +``` |
| 250 | +**Solution**: Build FlagCX and ensure `libflagcx.so` exists at `$FLAGCX_PATH/build/lib/libflagcx.so`, or use `--flagcx-lib-path` to specify the path. |
| 251 | + |
| 252 | +### ZMQ Connection Timeout |
| 253 | +**Solution**: Ensure both server and client use the same `--remote-ip` and `--zmq-port`. Check firewall rules. |
| 254 | + |
| 255 | +### CUDA Out of Memory |
| 256 | +**Solution**: Reduce `--sizes` or use `--device=cpu` for testing. |
| 257 | + |
| 258 | +## Plotting Results |
| 259 | + |
| 260 | +Use `plot_benchmark.py` to generate comparison figures from benchmark log files. |
| 261 | + |
| 262 | +### Save benchmark output to log files |
| 263 | + |
| 264 | +```bash |
| 265 | +# Run benchmarks and save output |
| 266 | +python3 kv_transfer_benchmark.py --connector=nixl --role=server \ |
| 267 | + --remote-ip=10.8.2.169 --device=gpu --nixl-backend=FLAGCX \ |
| 268 | + --sizes=1024,4096,16384,65536,262144,1048576,16777216 --iters=20 \ |
| 269 | + | tee nixl_flagcx.log |
| 270 | + |
| 271 | +python3 kv_transfer_benchmark.py --connector=mooncake --role=server \ |
| 272 | + --remote-ip=10.8.2.169 --device=gpu \ |
| 273 | + --sizes=1024,4096,16384,65536,262144,1048576,16777216 --iters=20 \ |
| 274 | + | tee mooncake.log |
| 275 | + |
| 276 | +python3 kv_transfer_benchmark.py --connector=flagcx --role=server \ |
| 277 | + --remote-ip=10.8.2.169 --device=gpu \ |
| 278 | + --sizes=1024,4096,16384,65536,262144,1048576,16777216 --iters=20 \ |
| 279 | + | tee flagcx.log |
| 280 | +``` |
| 281 | + |
| 282 | +### Generate comparison plot |
| 283 | + |
| 284 | +```bash |
| 285 | +# Auto-discover all *.log files in current directory |
| 286 | +python3 plot_benchmark.py |
| 287 | + |
| 288 | +# Specify log directory and output path |
| 289 | +python3 plot_benchmark.py --log-dir=./results --output=comparison.png |
| 290 | + |
| 291 | +# Display interactively |
| 292 | +python3 plot_benchmark.py --show |
| 293 | +``` |
| 294 | + |
| 295 | +The script auto-detects connector labels from log file headers (`connector=XXX`, `nixl-backend=YYY`) and produces a side-by-side figure with latency and bandwidth subplots. |
0 commit comments