Reproducible inference performance across modern accelerator platforms — real data, open lab.
Published findings appear first. These counters describe the open lab notebook, including planned work and documented failures; they are not a publication score. Browse study notes.
Matched disaggregated runs with prefill pinned to spark-01 and decode to spark-02. NVIDIA AIPerf 0.12.0, streaming chat, seed 42, exact-length synthetic prompts, one concurrency wave per point (two requests at c=1), fp8 KV, and DSpark speculative decoding. Prefix caching was disabled on both Dynamo and llm-d workers. Both paths performed remote prefill and transferred KV with NIXL; Dynamo used its frontend and llm-d used the decode-sidecar path.
| ISL / OSL | c | Dynamo tok/s | llm-d tok/s | Difference |
|---|---|---|---|---|
| 1K / 8K | 32 | 729.76 | 595.51 | Dynamo +22.5% |
| 8K / 8K | 32 | 629.86 | 526.60 | Dynamo +19.6% |
| 8K / 1K | 32 | 270.74 | 220.97 | Dynamo +22.5% |
Up and to the right is better: total output throughput is plotted against per-user output throughput. Select a workload to compare all six concurrency points.
Machine-readable 18-row matrices: Dynamo, llm-d, and combined comparison. This prefix-cache-off rerun supersedes the earlier cached Dynamo dataset. All 36 current comparison cells completed without request errors. Actual output lengths can differ slightly from requested lengths because of tokenizer and stop-token behavior. Request metrics are authoritative; sidecar server-counter resets produced telemetry warnings without benchmark request failures.
Single-replica control. Aggregated vLLM serving on separate 1× AMD Instinct MI300X instances. NVIDIA AIPerf 0.12.0, streaming chat, concurrency 4, four measured requests per point, fixed token lengths, seed 42. Dynamo path: frontend → vLLM worker. llm-d path: Envoy → EPP → vLLM. Use these results for per-replica capacity and wrapper overhead only; they do not test multi-worker routing, load balancing, horizontal scaling, or failover.
| ISL / OSL | Framework | Output tok/s | Avg TTFT | Avg ITL | Avg latency |
|---|---|---|---|---|---|
| 1K / 8K | Dynamo agg | 132.06 | 546.63 ms | 30.22 ms | 248.06 s |
| llm-d agg | 83.17 | 804.06 ms | 48.00 ms | 393.91 s | |
| 8K / 8K | Dynamo agg | 83.71 | 5,238.68 ms | 47.14 ms | 391.37 s |
| llm-d agg | 83.27 | 4,882.57 ms | 47.44 ms | 393.44 s | |
| 8K / 1K | Dynamo agg | 95.97 | 390.90 ms | 41.31 ms | 42.65 s |
| llm-d agg | 105.28 | 411.18 ms | 37.61 ms | 38.89 s |
Versions: Dynamo 1.4.2 with vLLM 0.28.1rc1.dev43; llm-d with vLLM 0.28.0. Both used --max-model-len 32768, GPU memory utilization 0.90, exact output lengths, and the same model. Four requests per point make this a controlled snapshot rather than a statistically robust capacity study.
Raw summary: mi300x-qwen38-aiperf.json
Partial run · capacity interrupted. Two MI300X workers per system, one prefill and one decode. Dynamo has 18/18 validated points; 5/18 llm-d points were recovered before the RunPod host became unavailable. Missing cells are pending and will be replaced when MI300X capacity returns.
| ISL / OSL | c | Dynamo tok/s | llm-d tok/s | llm-d mean TTFT | llm-d mean ITL | Status |
|---|---|---|---|---|---|---|
| 1K / 8K | 1 | 66.02 | 65.50 | 1,314.20 ms | 15.11 ms | Measured |
| 2 | 123.57 | 122.48 | 2,424.61 ms | 16.03 ms | Measured | |
| 4 | 213.55 | 211.64 | 4,640.94 ms | 18.34 ms | Measured | |
| 8 | 356.17 | 355.50 | 9,180.36 ms | 21.39 ms | Measured | |
| 16 | 620.59 | 616.07 | 17,645.34 ms | 23.60 ms | Measured | |
| 32 | 1,007.86 | — | — | — | Pending | |
| 8K / 8K | 1 | 61.93 | — | — | — | Pending |
| 2 | 114.22 | — | — | — | Pending | |
| 4 | 192.02 | — | — | — | Pending | |
| 8 | 310.49 | — | — | — | Pending | |
| 16 | 494.70 | — | — | — | Pending | |
| 32 | 738.25 | — | — | — | Pending | |
| 8K / 1K | 1 | 51.65 | — | — | — | Pending |
| 2 | 82.13 | — | — | — | Pending | |
| 4 | 121.75 | — | — | — | Pending | |
| 8 | 170.24 | — | — | — | Pending | |
| 16 | 217.73 | — | — | — | Pending | |
| 32 | 254.97 | — | — | — | Pending |
Validation: producer/consumer roles, matching NIXL compatibility hash, a successful 195.812 MiB remote KV transfer, and 0.0% local prefix-cache hits. Four additional jobs completed but their numeric artifacts were not recovered, so they remain pending.
Machine-readable data: mi300x-qwen38-disagg-partial.json
Single-replica control. Same model, same weights, same KV dtype, same speculative decoder — only the serving framework differs. Measured with NVIDIA AIPerf from the controller node, hitting each backend directly (no gateway in the path). Model: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-NVFP4 · modelopt_fp4 weights · fp8 KV · dspark spec-decode (3 tokens) · TP=1 · DGX Spark GB10 · ISL 1024 / OSL 256 · concurrency 1–32 · one model replica per arm. This curve measures per-replica capacity and wrapper overhead, not multi-worker routing or horizontal scaling.
Up and to the right is better: y is what the box produces in total, x is what a single user feels. Raising concurrency walks each curve up and to the left — you buy aggregate throughput with per-user speed.
All planned and completed experiments. Click any row to expand hypothesis and notes.
| Experiment | Framework | Status | Throughput (tok/s, c=32) |
TTFT p50 (ms) |
ITL p50 (ms) |
|---|
Lab notebook · not a published finding. Estimated deltas remain visible as hypotheses until the matched runs complete.
Progressive optimization stack on Qwen/Qwen2.5-7B-Instruct · vLLM standalone → llm-d · DGX Spark GB10 · spark-01 · ~150 tok input / 256 tok output · all concurrency levels (c=1, c=8, c=32)
Each point = one experiment. Path shows cumulative optimization journey. Select concurrency level above to switch view. Bottom-right = optimal (high throughput, low TTFT).
Marginal throughput and TTFT improvement each optimization adds vs the previous step. Model: Qwen/Qwen2.5-7B-Instruct · spark-01 GB10 · c=32 · ~150 tok input / 256 tok output
Throughput vs latency curves across concurrency levels (c=1,4,8,16,32) and input/output length combinations. Model: Qwen/Qwen2.5-7B-Instruct · Full-stack config (exp9) · DGX Spark GB10 · spark-02
Throughput vs latency across concurrency levels (c=1,2,4,8) and input/output length combinations. Model: nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4 · NVFP4 (Marlin backend, fp8 KV cache) · vllm-cu130-nightly · DGX Spark GB10 · spark-01 · single B200 · 2026-04-15
| ISL/OSL | Peak Throughput | TTFT p50 (c=1) | TTFT p99 (c=8) |
|---|---|---|---|
| ISL128/OSL128 | 79.7 tok/s | 341 ms | 1,127 ms |
| ISL512/OSL256 | 64.1 tok/s | 538 ms | 3,373 ms |
| ISL1024/OSL512 | 57.6 tok/s | 813 ms | 6,083 ms |
| ISL2048/OSL512 | 49.0 tok/s | 1,663 ms | 11,990 ms |
Throughput vs latency across concurrency levels (c=1,8,32) and input/output length combinations. Model: Qwen/Qwen3.5-27B-GPTQ-Int4 · GPTQ-Int4 (Marlin backend, enforce-eager) · vllm-cu130-nightly · DGX Spark GB10 · spark-01 · single B200 · 2026-04-17
| ISL/OSL | Peak Throughput | TTFT p50 (c=1) | ITL p50 (c=32) |
|---|---|---|---|
| ISL1024/OSL1024 | 104.1 tok/s | 1,197 ms | 179 ms |
| ISL4096/OSL1024 | 83.1 tok/s | 3,278 ms | 207 ms |
| ISL1024/OSL4096 | 59.6 tok/s | 459 ms | 134 ms |
SGLang vs TensorRT-LLM (PyTorch backend) vs llm-d (disaggregated vLLM) on identical workloads. Model: Qwen/Qwen2.5-7B-Instruct · TP=1 · DGX Spark GB10 · 5 ISL/OSL combos × 5 concurrency levels
Lab notebook · data pending. No deployment recommendation is made from this section.
Throughput delta relative to baseline for each optimization. Green = improvement, red = regression. Model: Qwen/Qwen2.5-7B-Instruct · DGX Spark GB10 · enforce_eager → CUDA graphs