要点速览
- 默认 pull 模式下,router 中继、decode 端分配与传输准备都串行发生在 prefill 之后,全部落在 TTFT 关键路径上。
- push 模式让 decode 在分配块时即注册目标地址,prefill 算完 KV 后由后台线程发起 NIXL WRITE 直写 decode 显存。
- Qwen3-32B、1P1D、P5en+EFA 实测:TTFT 最高提升 3 倍,TPOT 最高改善 30%,高负载长输入下差距最大。
- 代价是 push 需更早选定 decode 实例,削弱 load-aware routing 灵活性;需要双向 KV 传输时仍须用 pull 模式。
- 两侧 kv_connector 由 NixlConnector 换成 NixlPushConnector 即启用(vLLM 0.29.0 已含)。
解决什么问题
P-D 分离架构要求在 decode 开始生成前把 KV cache 从 prefill 实例传到 decode 实例,传输效率直接决定 TTFT。默认的 pull-based NIXL connector 要等 prefill 完成后,才由 router 中继源元数据、decode 分配目标块并准备和发起 NIXL READ,这些串行的协调与设置步骤都落在 TTFT 关键路径上。高请求率下这些开销还在 decode 引擎循环内同步执行,进一步拖慢逐 token 生成(TPOT)。
核心思路
核心思路是把传输协调与设置移出关键路径,并让生产者与消费者的工作重叠。decode 实例在为本请求分配本地块的同一步内,把目标块位置以 NIXL notification 注册给 prefill;prefill 侧用 rendezvous 机制配对『注册到达』与『prefill 完成』两个事件(先后任意),二者齐备即触发传输。传输由 prefill 的专用后台线程执行 NIXL WRITE,把 KV 直接写入 decode 已注册的显存,并按 tensor-parallel rank 逐一寻址,每笔 WRITE 附带完成通知。decode 按生产者 rank 计数通知,收齐全部预期通知后才把请求送入 decode 阶段。两侧在传输时各自把块位置映射到物理布局,因此允许不同的内部 block size。
关键要点
- Pull 模式的串行关键路径 — 默认 NIXL connector 是 pull-based:prefill 算完 prompt KV 后,router 先把含 prefill block IDs 的源元数据中继给 decode;decode 再分配目标块、准备 NIXL 传输并发起 NIXL READ 拉取。这些协调与设置步骤串行发生在 prefill 之后、decode 开始之前,全部计入 TTFT。
- Push 传输三步协议:注册、会合、传输 — Registration:decode 在为请求分配本地块的同一步内,把自身身份与目标块位置作为 NIXL notification 发给 prefill,因此 prefill 完成前 connector 就已知目的地。Rendezvous:注册与 prefill 完成可任意先后到达,connector 按请求标识符配对并正确关联被抢占或重发的请求,后到的事件触发传输。Transfer:prefill 建立(或复用)连接后发起 NIXL WRITE,把 KV 直接写进 decode 已注册的内存。
- Prefill 侧后台线程模型 — 每个 prefill worker 配一个专用后台 transfer 线程:主循环在注册与 prompt KV 都就绪后,把 KV 块交给后台线程并立即返回模型执行。后台线程负责建立或复用 NIXL 连接、把 block ID 映射为内存描述符、将连续块合并成更大的传输区域并提交 NIXL WRITE。描述符准备与传输提交因此不再占用延迟敏感的主循环。
- 完成通知与布局无关的块映射 — 每笔 NIXL WRITE 携带完成通知,数据到达后由 NIXL 投递给 decode;decode 对每个写入它的 producer rank 各计一次通知,收齐全部预期通知才认为 KV 就绪并进入 decode。生产者与消费者可使用不同内部块布局,双方在传输时各自把块位置映射到自己的物理布局,因此两侧 block size 可以不同。
- Push 与 Pull 的路由权衡 — Pull 允许 router 在 prefill 完成后再选 decode 实例,对 load-aware routing 与 QoS 管理更灵活;push 必须更早选定 decode,才能让目标注册与 prefill 重叠。回报是在可比负载下 prefill 能更早释放 KV 块(prompt KV 与目标注册齐备即开始传输)。另外当前 push 不支持双向 KV 传输,需要该能力的负载仍应选 pull。
- 性能结果与归因 — 输入 1k–16k、请求率 1–8 QPS 扫描下,低载短 prompt 两模式几乎相同(TPOT 约 5.6–5.8 ms);随 QPS 与输入增长 pull 急剧退化,8 QPS/16k 时 TTFT 约 1,744 ms、TPOT 约 10.4 ms,push 分别保持约 540 ms 与约 7 ms。原因是 pull 在 decode worker 引擎循环内同步 post NIXL 描述符、阻塞 token 生成,push 把这些挪到了后台线程。
- 基准方法学:隔离 P→D 传输 — 评测用 --no-enable-prefix-caching 关闭前缀缓存,迫使 decode 每个请求都从 prefill 取全量 KV;否则重叠 prompt 会命中 decode 本地缓存、绕过传输,使对比失真。环境为两台 p5en.48xlarge(8×H200)加一台 proxy/benchmark 节点,vLLM 0.29.0、nixl 1.4.1,NIXL 走 LIBFABRIC/EFA 后端,benchmark 脚本在 vLLM PR #57567。
- 部署切换与故障排查 — 两种模式的差别只在 kv_connector:pull 用 NixlConnector、push 用 NixlPushConnector,且 P/D 两侧必须一致,否则首个请求即崩溃。push 的 proxy 还需要 prefill 实例的 NIXL 坐标(PREFILL_ENGINE_ID、PREFILL_KV_HOST、PREFILL_SIDE_CHANNEL_PORT、PREFILL_TP_SIZE、PREFILL_PP_SIZE)以便告诉 decode 向哪里注册。NIXL 握手或传输报错时先检查 EFA provider(fi_info -p efa)、5600 side-channel 端口连通性与 PREFILL_TP_SIZE 是否匹配。
关键数据
| 指标 | 数值 | 条件 |
|---|---|---|
| TTFT 改善倍数(push vs pull) | 最高 3x | Qwen3-32B,1P1D 部署,Amazon EC2 P5en(8×H200)+ EFA,128 输出 token,decode 侧关闭 prefix caching,输入 1k–16k、请求率 1–8 QPS 扫描 |
| TPOT 改善幅度(push vs pull) | 最高 30% | Qwen3-32B,1P1D,P5en+EFA,128 输出 token,decode 侧关闭 prefix caching,高请求率与长输入下收益最大 |
| 高负载 TTFT(pull 模式) | 约 1,744 ms @ 8 QPS、16k 输入 | Qwen3-32B,1P1D,P5en+EFA,128 输出 token,prefix caching 关闭,输入长度与请求率联合扫描 |
| 高负载 TTFT(push 模式) | 约 540 ms @ 8 QPS、16k 输入(约 3 倍改善) | Qwen3-32B,1P1D,P5en+EFA,与 pull 相同的 8 QPS、16k 工作点 |
| TPOT(pull 模式,高负载) | 约 10.4 ms @ 4–8 QPS、16k 输入 | Qwen3-32B,1P1D,P5en+EFA,128 输出 token;pull 在 decode 引擎循环内同步做描述符准备导致退化 |
| TPOT(push 模式,高负载) | 约 7 ms | Qwen3-32B,1P1D,P5en+EFA,4–8 QPS、16k 输入;描述符准备被卸载到后台线程,token 生成不被饿死 |
| 轻载 TPOT(两模式基线) | 约 5.6–5.8 ms | Qwen3-32B,1P1D,P5en+EFA,低负载、短 prompt 下 push 与 pull 几乎无差别 |
| 单实例 EFA 网络带宽 | 3.2 Tb/s(16 块网卡 × 200 Gb/s) | p5en.48xlarge(8×H200)单实例;每 GPU 与 2 块 EFA 网卡共享 PCIe switch,NIXL 只在 PCIe 本地的 rail 上注册 KV cache |
“In our benchmark on a 1P1D deployment using Amazon EC2 P5en instances connected with EFA, push mode delivered up to 3x better TTFT and up to 30% better time-per-output-token (TPOT) compared to pull mode (see the [Performance data](#performance-data) section) for Qwen3-32B model; results depend on workload and configuration.”
“push mode delivered up to 3x better TTFT and up to 30% better time-per-output-token (TPOT) compared to pull mode”
“As QPS and input length grow, pull mode degrades sharply, reaching about 1,744 ms at 8 QPS and 16k.”
前置知识
LLM 推理两阶段:prefill 处理 prompt 并构建 KV cache,decode 逐 token 生成 · Prefill-Decode (P-D) disaggregation 的动机与 vLLM disaggregated serving 基本用法 · KV cache 的分页块管理:block IDs、block table 与按 tensor-parallel rank 划分 · RDMA 基本概念,以及 AWS EFA、GPUDirect RDMA / Neuron Direct RDMA 设备间直传 · NIXL connector 默认 pull 模式的工作流(router 中继元数据 + decode 发起 NIXL READ) · TTFT / TPOT / ITL 等推理服务延迟指标的定义
术语表
push-based transfer: 由生产方(prefill)主动发起 NIXL WRITE、把数据写入消费方(decode)预先注册内存的传输模式,目标注册可与 prefill 计算重叠。pull-based transfer: NIXL connector 默认模式:消费方(decode)收到 router 中继的源元数据后,自行分配目标块并发起 NIXL READ 拉取 KV。NIXL: NVIDIA Inference Xfer Library,vLLM 用于跨引擎实例传输 KV block 的库,可经 RDMA/libfabric 后端(如 AWS EFA)在设备内存间直接搬数据。RDMA: Remote Direct Memory Access,绕过远端主机 CPU 与内核网络栈直接读写远端内存的网络能力。AWS EFA: Elastic Fabric Adapter,AWS 跨服务器的低延迟 RDMA 网络;在支持的实例上提供 GPUDirect RDMA 与 Neuron Direct RDMA。GPUDirect RDMA: 网卡与 GPU 显存间直接传输、无需经主机内存中转的技术,使 NIXL 可在 GPU 或 Trainium 设备内存间直传 KV。rendezvous: push 协议中配对两个异步事件(decode 注册到达、prefill 计算完成)的机制;无论谁先到,后到的事件触发传输。TTFT: Time To First Token,从请求到第一个 token 的延迟,是 P-D 分离中 KV 传输效率的关键指标。TPOT: Time Per Output Token,平均每个输出 token 的生成时间,反映 decode 阶段的吞吐与延迟稳定性。
Push-Based KV Transfer to Improve TTFT and TPOT for Disaggregated Inference in vLLM
vLLM is an open-source inference and serving engine for large language models (LLMs). LLM inference has two distinct phases: prefill, which processes the input prompt and builds the key-value (KV) cache, and decode, which uses that cache to generate output one token at a time. These phases have different compute, memory, and scaling characteristics. Prefill-decode (P-D) disaggregation runs them on separate engine instances, allowing each phase to scale and be optimized independently. This can improve resource utilization, throughput, and latency under load. However, it also requires transferring the KV cache from the prefill instance to the decode instance before token generation can begin, making efficient KV transfer critical to time-to-first-token (TTFT).
One of the ways vLLM performs this transfer is through the NVIDIA Inference Xfer Library (NIXL) connector, which moves KV blocks over RDMA-capable interconnects such as AWS Elastic Fabric Adapter (AWS EFA, the low latency RDMA network used across servers in AWS). On supported instance types, AWS EFA supports GPUDirect RDMA and Neuron Direct RDMA, enabling NIXL to transfer KV blocks directly between GPU or Trainium device memory without staging through host memory. The default mode for NIXL connector is pull-based: After the prefill instance finishes computing the prompt KV cache, the router relays the source metadata, including the prefill block IDs, to the decode instance. The decode instance then allocates destination blocks, prepares the NIXL transfer, and initiates NIXL READ to pull the KV blocks from the prefill instance. Because these coordination and setup steps occur sequentially after prefill and before decoding can begin, they extend the TTFT critical path, as shown in the following timeline.
This post introduces a push-based NIXL connector that moves post-prefill transfer coordination and setup off the critical path. For customers running disaggregated prefill/decode inference with vLLM, this can improve application responsiveness by reducing TTFT and TPOT, particularly at high request rates.
In our benchmark on a 1P1D deployment using Amazon EC2 P5en instances connected with EFA, push mode delivered up to 3x better TTFT and up to 30% better time-per-output-token (TPOT) compared to pull mode (see the Performance data section) for Qwen3-32B model; results depend on workload and configuration.
We built and validated the feature on AWS Trainium instance clusters connected with EFA, and then we contributed it to vLLM as an accelerator-agnostic feature. The results here are from an AWS p5en (GPU) cluster with EFA, showing the gains carry over to GPUs. This post explains the push-based architecture, transfer flow, threading model, performance results, and how to reproduce the benchmark.
Push-based KV transfer
Architecture overview
Push-based transfer improves TTFT and decode throughput for two reasons:
- It removes the serialization of the pull model by overlapping producer and consumer work. The decode instance registers its target blocks in advance while the producer is still computing KV. The transfer then begins as soon as the producer's blocks are ready, rather than after a consumer-side identifier-discovery round trip, removing that round trip from the path to first token.
- It moves the transfer-setup time off the decode instance. That setup — preparing the NIXL descriptors and submitting the transfer — runs on the prefill side and on a background thread, so the decode instance does not spend those cycles during its latency-critical token-generation phase.
The net effect is lower TTFT and higher output-token throughput on the decode instance.
The push connector coordinates three actors: the decode instance, which registers its target blocks up front; the prefill instance, which matches each request and issues the transfer; and a dedicated background thread that carries the KV write off the engine's forward-pass loop. The following diagram shows how these pieces fit together.
Implementation details
The protocol has three logical steps.
1. Registration (decode side). When the decode instance allocates local blocks for a request requiring remote prefilling, it prepares a registration describing where the prefill instance should write the KV: its own identity and the block locations it has just reserved. Within the same engine step, the worker transmits this registration to the prefill instance as a NIXL notification. Because registration happens at allocation time, the connector knows the destination blocks before the prefill instance completes prefill.
2. Rendezvous (prefill side). The registration from the decoder and the completion of prefill on the producer can arrive in either order, and the connector handles both. If the registration arrives first, the producer holds it until its own prefill finishes. If prefill finishes first, the producer holds the request's blocks until the registration arrives. Whichever event happens second triggers the transfer. The connector pairs requests by identifier, and the matching accounts for reissued requests, correctly associating preempted or retried requests.
3. Transfer (prefill side). Once a request is matched, the producer establishes a connection to the decode instance (if one does not already exist) and issues an NIXL WRITE that copies the KV blocks directly into the decoder's registered memory, addressing each destination tensor-parallel rank. The producer and consumer may use different internal block layouts. Each side maps block locations to its own physical layout at transfer time, allowing different block sizes on each side. Each WRITE carries a completion notification that NIXL delivers to the decode instance once the data arrives. The decoder counts these notifications, one per producer rank that writes into it, and treats the KV as received only after all expected notifications have arrived, at which point the request proceeds to decode.
Threading model
The push connector separates model execution from KV-transfer preparation by using a dedicated background transfer thread on each prefill worker. At the start of a request, the decode main loop allocates its destination KV blocks and registers them with the prefill instance while the prefill main loop computes the prompt KV cache. After both the destination registration and prompt KV are ready, the prefill main loop hands the KV blocks to the background thread and quickly returns to model execution.
The background thread establishes or reuses the NIXL connection, maps the request's KV block IDs to memory descriptors, merges contiguous blocks into larger transfer regions, and submits the NIXL WRITE to the decode instance. When NIXL completes the transfer, it sends a completion notification to the decode instance, which then begins token generation. Keeping descriptor preparation and transfer submission off the latency-critical main loops prevents this per-request work from delaying model execution. The following diagram shows these main-loop and background-thread handoffs for a single request.
Timing comparison: Pull vs push
The following diagram summarizes the pull and push transfer flows described in the preceding sections and highlights their effect on TTFT. Although both modes use the same NIXL and EFA/RDMA data path, pull performs the metadata relay and decode-side setup after prefill, while push overlaps decode-side setup with prefill. As a result, push removes these operations from the post-prefill critical path, allowing decoding to begin sooner.
The two modes also involve a routing tradeoff. Pull mode allows the router to select a decode instance after prefill completes, which provides greater flexibility for load-aware routing and quality-of-service management. Push mode selects the decode instance earlier so that destination setup can overlap with prefill. In return, under comparable load, the prefill instance can release its KV blocks sooner because the transfer starts as soon as both the prompt KV and destination registration are ready.
Performance data
For evaluation, we used Qwen3-32B model on a 1P1D deployment using Amazon EC2 P5en instances connected with EFA. To isolate P→D transfer performance, we configured the benchmark to disable prefix caching with --no-enable-prefix-caching. This configuration makes the decode node retrieve the full KV cache from the prefill node for every request. Otherwise, overlapping prompts could reuse decode node's local cache and bypass the transfer, which would produce misleading comparison results.
The following chart compares TTFT for push and pull KV transfer. We sweep input lengths 1k–16k and request rates 1–8 QPS. At low load and short prompts, the two modes perform almost identically. As QPS and input length grow, pull mode degrades sharply, reaching about 1,744 ms at 8 QPS and 16k. Push mode stays far lower at roughly 540 ms, around 3x reduction. The gap opens because pull mode posts NIXL descriptors inline on the decode worker, synchronously within its engine loop, so that work stalls token generation. Push mode moves it to a background thread, off the critical path.
This chart shows TPOT under the same conditions. The pattern mirrors TTFT. At light load, push and pull are close, around 5.6–5.8 ms. As load and input length rise, pull climbs to about 10.4 ms at 4–8 QPS and 16k, while push holds near 7 ms. The reason is that push offloads descriptor preparation and transfer submission from the decode worker. Token generation is never starved, so per-token latency stays flat even at high concurrency and long contexts.
How to run benchmark
To reproduce our 1P1D benchmark configuration, one prefill node and one decode node, using Amazon EC2 P5en instances connected with EFA, refer to the detailed instructions in the Reproducing our results guide. The guide covers environment setup, launching the vLLM prefill and decode servers, running the benchmark, and collecting results. The feature can run on other supported configurations, but performance results will vary depending on the hardware, model, workload, and network configuration.
Conclusion
In this post, we showed how the push-based KV connector overlaps prefill computation with decode-side block allocation and registration, removing the post-prefill router round trip and decode-side transfer setup from the critical path. This reduces TTFT and TPOT, particularly at high request rates. In our Qwen3-32B benchmark on a 1P1D deployment using Amazon EC2 P5en instances connected with EFA, push mode provided up to a 3x improvement in TTFT and 30% improvement in TPOT compared with pull mode. Results vary by workload and configuration. To use push mode, configure both the prefill and decode servers to use NixlPushConnector instead of NixlConnector.
Choose the push-based connector when early decode node selection is acceptable and lower TTFT is the priority. The pull-based connector remains the better fit for workloads that require bidirectional KV transfer or defer decode-instance selection until after prefill for load-aware routing. vLLM supports both push- and pull-based KV connectors, allowing users to select the approach that best fits their deployment.
As a future improvement, we are exploring bringing the background NIXL threading model to the pull-mode connector, moving NIXL setup off the critical path so that pull-mode workloads could benefit from the same optimizations; we have opened a pull request (#45211) to explore this.
Related resources
Acknowledgements
We thank the vLLM community for their support in maintaining this feature.
Appendix: Reproducing our results
The topology is two p5en GPU nodes (prefill on :8100, decode on :8200) with EFA enabled, plus a third smaller node that runs the proxy and the benchmark client. Three scripts ship with this post: serve_pd.sh launches either leg in either mode, run_sweep.sh drives the sweep, and cleanup.sh tears everything down. plot_push_vs_pull.py redraws the two figures above from the result JSONs.
Test environment
Both GPU nodes were configured identically:
| Instance type | p5en.48xlarge (8x NVIDIA H200), one for prefill and one for decode |
| Region / AZ | ap-south-1 (Mumbai), ap-south-1b (AZ ID aps1-az3), both nodes in the same AZ |
| Placement | No cluster placement group; both instances launched into a targeted On-Demand Capacity Reservation, default tenancy |
| AMI | Public AWS ParallelCluster 3.14.2 image for Ubuntu 24.04 x86_64, aws-parallelcluster-3.14.2-ubuntu-2404-lts-hvm-x86_64-202602121713 (ami-0133f195033a4baae in ap-south-1; AMI IDs differ by region). It ships kernel 6.14.0-1018-aws, NVIDIA driver 570, and CUDA 12.8, so upgrade the driver, CUDA toolkit, and EFA installer to the versions below. |
| OS / kernel | Ubuntu 24.04.4 LTS, 6.14.0-1018-aws |
| NVIDIA driver / CUDA | Driver 580.126.16 (CUDA 13.0 driver API); CUDA toolkit 13.0 |
| EFA interfaces | 16 per instance, one per network card (cards 0-15, all interface type efa), 200 Gb/s each for 3.2 Tb/s total. 8 interfaces sit on each NUMA node, and each GPU shares a PCIe switch with 2 of them. |
| EFA software | EFA installer 1.50.0, EFA kernel module 3.3.0g, libfabric 2.6.0amzn1.0, rdma-core 64.0amzn0 |
EFA device selection by NIXL. The LIBFABRIC backend opens all 16 EFA devices as rails with the efa provider. It then registers each GPU's KV cache only on the 2 rails that share that GPU's PCIe switch, so every transfer stays PCIe-local. No NIXL or libfabric device overrides were set; this is the default selection. To confirm it on your own nodes, launch the servers with NIXL_LOG_LEVEL=DEBUG and check the registration lines:
grep -E "Created rail [0-9]+ \(device=|mapped to rail" server.log
1. Provision the nodes
Install the EFA userspace stack on both GPU nodes and confirm the provider is present. We used installer 1.50.0:
curl -O https://efa-installer.amazonaws.com/aws-efa-installer-1.50.0.tar.gz
tar -xf aws-efa-installer-1.50.0.tar.gz && cd aws-efa-installer
sudo ./efa_installer.sh -y
fi_info --version # libfabric 2.6.0amzn1.0
fi_info -p efa # verify the EFA provider is present
The two GPU nodes must reach each other on 8100/8200 and on the NIXL side channel (5600).
2. Install vLLM and NIXL
On both GPU nodes:
python3.12 -m venv ~/vllm_env && source ~/vllm_env/bin/activate
pip install --upgrade pip
pip install vllm==0.29.0 # any build that includes the NIXL push connector
pip install nixl==1.4.1 # NIXL with the libfabric/EFA backend
On the proxy node (no GPU runtime needed):
pip install vllm aiohttp fastapi uvicorn pandas datasets
3. Download the model
export HF_HOME=/shared/huggingface # a large shared volume
hf download Qwen/Qwen3-32B
4. Launch the P/D pair
Run serve_pd.sh once per GPU node. The only difference between the two modes is kv_connector: NixlConnector for pull, NixlPushConnector for push. Ports, engine IDs, roles, and ranks stay the same.
# on the prefill node
PREFILL_IP=<PREFILL_IP> DECODE_IP=<DECODE_IP> ROLE=prefill MODE=pull ./serve_pd.sh
# on the decode node
PREFILL_IP=<PREFILL_IP> DECODE_IP=<DECODE_IP> ROLE=decode MODE=pull ./serve_pd.sh
Which expands to, on the prefill side:
vllm serve Qwen/Qwen3-32B --served-model-name Qwen --dtype bfloat16 \
--tensor-parallel-size 8 --max-model-len 32768 --max-num-seqs 8 --port 8100 \
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_buffer_device":"cuda",
"kv_role":"kv_producer","engine_id":"prefill-engine-001","kv_rank":0,
"kv_parallel_size":2,"kv_connector_extra_config":{"backends":["LIBFABRIC"]}}'
The decode server adds --no-enable-prefix-caching, which forces every request to fetch full KV from prefill — exactly what we are measuring. With caching on, overlapping prompts could hit the decode node's local cache and skip the P->D transfer, giving misleading numbers. Wait for both /health endpoints to return 200 before benchmarking.
5. Run the sweep
The benchmark script lives in #57567; it picks the proxy that matches the mode (disagg_proxy_demo.py for pull, disagg_proxy_pushconnector_demo.py for push) and drives it with vllm bench serve.
git clone https://github.com/vllm-project/vllm.git ~/vllm && cd ~/vllm
git fetch origin pull/57567/head:pd-bench && git checkout pd-bench
export VLLM_SRC=~/vllm
VLLM_SRC=~/vllm PREFILL_IP=<PREFILL_IP> DECODE_IP=<DECODE_IP> \
MODE=pull ./run_sweep.sh
Then stop both servers, relaunch step 4 with MODE=push on each node, and rerun the sweep with MODE=push. Push mode additionally needs the prefill instance's NIXL coordinates (PREFILL_ENGINE_ID, PREFILL_KV_HOST, PREFILL_SIDE_CHANNEL_PORT, PREFILL_TP_SIZE, PREFILL_PP_SIZE) so the proxy can tell the decode node where to register; run_sweep.sh fills these in from the defaults used above. Keep QPS_LIST, INPUT_LENS, and OUTPUT_LENS identical across the two runs, and make sure PREFILL_TP_SIZE matches the prefill server's actual tensor-parallel size.
results/ holds <mode>_qps<q>_in<in>_out<out>_iter<i>.json (plus .log) with per-run TTFT, ITL, throughput, and end-to-end latency. To redraw the figures:
python plot_push_vs_pull.py --results-dir ./results --metric ttft
python plot_push_vs_pull.py --results-dir ./results --metric tpot
Troubleshooting
- Server crashes right after the first request — the mode doesn't match the connector. Pull needs
NixlConnector; push needsNixlPushConnector, on both P and D. Backend 404: model ... does not exist—SERVED_MODEL_NAMEdoesn't match the servers'--served-model-name(Qwen).- Proxy readiness times out — confirm the proxy for the chosen mode exists under
$VLLM_SRC/examples/disaggregated/disaggregated_serving/, and that both P and D/healthreturn 200. - NIXL handshake or transfer errors — verify EFA (
fi_info -p efa), that the side-channel port (5600) is open between P and D, and thatPREFILL_TP_SIZEmatches the prefill server's tensor-parallel size.
Cleanup
./cleanup.sh proxy # on the proxy / benchmark driver node
./cleanup.sh server # on each GPU node
Then follow the AWS EC2 user guide to terminate the instances.