Split View: PD 분리는 처리량을 늘리지 않는다 — 프리필/디코드 분리가 실제로 사는 것
PD 분리는 처리량을 늘리지 않는다 — 프리필/디코드 분리가 실제로 사는 것
- 들어가며 — 한 GPU에 사는, 성격이 다른 두 개의 일
- 왜 한 GPU에 두면 문제인가 — 간섭
- 그래서 분리한다 — PD 분리의 구조
- 숫자를 읽는 법 — 처리량이 아니라 goodput
- 그 배수들을 실제로 뜯어보면
- KV를 옮기는 배관 — 진짜 엔지니어링은 여기 있다
- 2026년 4월, MORI-IO — 구체적인 측정 하나
- 공짜가 아니다 — 정직한 비용
- 그래서, 당신은 써야 하나
- 마치며
- 참고 자료
들어가며 — 한 GPU에 사는, 성격이 다른 두 개의 일
LLM 추론이 두 단계로 나뉜다는 건 이제 상식입니다. 프롬프트 전체를 한 번의 포워드 패스로 밀어 넣어 KV 캐시를 만드는 프리필(prefill), 그리고 토큰을 하나씩 뽑는 디코드(decode). KV 캐시 이야기를 할 때마다 나오는 그 두 단계입니다.
중요한 건 이 둘의 성격이 정반대라는 점입니다. SGLang 문서의 표현을 그대로 옮기면, 프리필은 입력 시퀀스 전체를 처리하는 연산 집약적(computation-intensive) 작업이고, 디코드는 KV 캐시를 관리하는 메모리 집약적(memory-intensive) 작업입니다. 프리필은 GPU의 연산 유닛을 태우고, 디코드는 매 토큰마다 모델 가중치와 KV 캐시 전체를 다시 읽느라 메모리 대역폭에 묶입니다.
성격이 다른 두 작업을 같은 GPU에 올려놓고 같은 스케줄러로 돌리면 무슨 일이 생길까요. 그리고 쪼개면 정말 나아질까요. 이 글은 그 "쪼개기" — PD 분리(prefill/decode disaggregation) — 가 2026년에 어디까지 왔는지, 그리고 왜 인터넷에 돌아다니는 배수 숫자들을 곧이곧대로 읽으면 안 되는지에 대한 이야기입니다.
왜 한 GPU에 두면 문제인가 — 간섭
SGLang 문서는 통합 엔진에서 생기는 문제를 두 가지로 못 박습니다.
첫째, 프리필 인터럽션(Prefill Interruption). 문서의 문장 그대로는 "들어오는 프리필 배치가 진행 중인 디코드 배치를 자주 중단시켜 토큰 생성에 상당한 지연을 유발한다"입니다. 그림을 그려 보면 명확합니다. 어떤 사용자가 토큰을 한 개씩 받으며 스트리밍 중인데, 옆에서 8000토큰짜리 프롬프트가 하나 도착합니다. 그 프리필은 GPU를 한동안 통째로 점유하고, 스트리밍 중이던 사용자의 다음 토큰은 그만큼 늦게 나옵니다. 사용자 입장에서는 글자가 흐르다가 뚝 멈추는 순간입니다.
둘째, DP 어텐션 불균형(DP Attention Imbalance). 데이터 병렬 어텐션에서 한 DP 워커는 프리필 배치를, 다른 워커는 디코드 배치를 동시에 처리하게 되면 디코드 지연이 늘어납니다.
TensorRT-LLM의 기술 블로그도 같은 진단을 내립니다 — 통합 서빙에서는 "컨텍스트 처리가 토큰 생성을 지연시키는 간섭"이 생기고, 그래서 상호작용성이 떨어진다는 것입니다. 그리고 이 블로그는 문제의 본질을 한 문장으로 정리합니다. "TTFT 같은 한 지표를 최적화하면 TPOT 같은 다른 지표를 희생하게 되는 일이 잦다."
이게 핵심입니다. 한 GPU 위에서 프리필과 디코드는 같은 자원을 두고 경쟁하는 두 개의 상충하는 목표입니다. 배치를 키우면 처리량은 오르지만 꼬리 지연이 무너지고, 프리필을 잘게 쪼개면(청크드 프리필) 간섭은 줄지만 프리필 효율이 떨어집니다. 하나의 스케줄러로 두 목표를 동시에 만족시키려는 시도 자체가 구조적으로 타협입니다.
그래서 분리한다 — PD 분리의 구조
발상은 단순합니다. 성격이 다르니 하드웨어를 따로 주자.
vLLM 문서의 정의로는, 분리 프리필은 프리필과 디코드 단계를 서로 다른 vLLM 인스턴스에서 돌리고, 그 사이에서 KV 캐시를 커넥터로 옮기는 것입니다. 실제 배치는 대략 이렇게 생겼습니다.
[ 프록시 / 라우터 ]
| |
요청 ---->| |
v v
[ 프리필 인스턴스 ] [ 디코드 인스턴스 ]
GPU 0-3 GPU 4-7
프롬프트 -> KV KV -> 토큰 스트림
| ^
| KV 캐시 |
+--- RDMA --+
(커넥터)
세 조각이 필요합니다.
- 프리필 인스턴스 — 프롬프트를 받아 KV 캐시를 만든다.
- 디코드 인스턴스 — 그 KV 캐시를 받아 토큰을 뽑는다.
- 커넥터와 프록시 — KV 캐시를 실어 나르고, 요청을 어느 쪽으로 보낼지 조율한다.
이제 각 단계에 병렬화 전략을 따로 걸 수 있습니다. 프리필에는 TP를 낮게, 디코드에는 높게 — 혹은 그 반대로. vLLM 문서가 이 기능의 이점으로 첫 번째로 꼽는 게 정확히 이것입니다. "TTFT와 ITL을 따로 튜닝하기." 그리고 두 번째는 "꼬리 ITL 통제하기"입니다. 프리필 잡이 디코드를 방해할 수 없으니, 디코드 GPU 위에서 토큰은 일정한 리듬으로 나옵니다.
vLLM에서 이 기능은 아직 experimental로 표시돼 있습니다. 문서의 문장 그대로 "이 기능은 실험적이며 변경될 수 있습니다."
숫자를 읽는 법 — 처리량이 아니라 goodput
여기가 이 글의 핵심입니다.
PD 분리를 검색하면 "2배", "7배" 같은 숫자가 쏟아집니다. 그런데 정작 vLLM 공식 문서에는 이런 문장이 대문자로 박혀 있습니다.
"Disaggregated prefill DOES NOT improve throughput." (분리 프리필은 처리량을 개선하지 않습니다.)
모순처럼 보입니다. 하지만 둘 다 맞습니다. 이유를 알면 PD 분리를 보는 눈이 완전히 바뀝니다.
생각해 보면 당연합니다. PD 분리는 GPU를 더 만들어 주지 않습니다. 8장의 GPU를 4장은 프리필, 4장은 디코드로 나눴다고 해서 총 연산량이 늘어날 리 없습니다. 오히려 KV 캐시를 네트워크로 옮기는 일이 새로 생겼으니, 순수한 총 작업량으로 따지면 늘어납니다. TensorRT-LLM 블로그도 이걸 인정합니다 — "분리는 KV 캐시 블록을 전송하는 오버헤드를 수반한다."
그러면 그 배수들은 뭘 잰 걸까요. 답은 goodput입니다.
이 개념의 출처는 PD 분리의 원조 논문인 DistServe(Zhong 등, OSDI 2024)입니다. 제목부터가 "Goodput-optimized"입니다. goodput은 대략 이렇게 정의됩니다 — TTFT와 TPOT 제약을 둘 다 만족시키면서 감당할 수 있는 최대 요청률. 그냥 처리량이 "초당 몇 개를 뱉었나"라면, goodput은 "초당 몇 개를 약속한 품질로 뱉었나"입니다.
이 차이가 얼마나 큰지는 vLLM에 들어온 MORI-IO 커넥터 블로그가 명확히 보여 줍니다. 그 블로그의 goodput 정의는 이렇습니다.
"Goodput = maximum request rate (req/s) such that requests satisfy both TTFT < T_ttft and ITL < T_itl."
즉 SLO를 못 지킨 요청은 세지 않습니다. 처리량 계산에서는 3초 걸린 TTFT도 한 건이지만, goodput 계산에서는 0건입니다. 사용자가 이미 탭을 닫았을 테니까요.
그래서 정리하면 이렇습니다.
- 처리량(순수 tokens/s, 지연 무시): PD 분리는 개선하지 않는다. 오히려 전송 오버헤드로 약간 손해다. — vLLM 문서가 말하는 게 이것.
- goodput(SLO를 지킨 처리량): PD 분리가 크게 개선한다. — 벤더 블로그들이 재는 게 이것.
같은 시스템, 다른 자. 인터넷의 배수를 볼 때 어느 자로 쟀는지를 먼저 확인해야 하는 이유입니다.
그 배수들을 실제로 뜯어보면
이 렌즈로 공개된 숫자들을 다시 읽어 봅시다. 아래는 전부 벤더/저자 자체 측정이며, 각 측정의 조건을 함께 적습니다. 조건이 곧 숫자의 의미입니다.
DistServe (Zhong 등, OSDI 2024, 저자 자체 측정). 논문의 헤드라인은 "7.4배 더 많은 요청" 또는 "12.6배 더 빡빡한 SLO"입니다. 그런데 바로 뒤에 조건이 붙습니다 — "요청의 90퍼센트 초과에 대해 지연 제약 안에 머물면서". 이건 처리량 배수가 아니라 SLO 달성률을 고정한 상태의 요청률 배수입니다. 그리고 배수는 모델·애플리케이션·지연 요구사항에 따라 달라진다고 논문 스스로 말합니다.
TensorRT-LLM (NVIDIA 자체 측정, GB200). 여기 숫자가 특히 교훈적입니다. DeepSeek R1에서 입력 4400 / 출력 1200 조건일 때 MTP 없이 1.4~1.8배, MTP를 켜면 1.6~2.5배. 입력 8192 / 출력 256에서는 4-GPU 최대 1.73배, 8-GPU 2배. 그리고 결정적인 항목 — 입력 4096 / 출력 1024에서 "사용자당 초당 50토큰 지점에서 1.7배 개선". 마지막 문장을 다시 읽어 보십시오. 사용자당 초당 50토큰이라는 지연 목표를 고정한 상태에서의 처리량 배수입니다. 지연 제약을 풀면 이 배수는 그대로 있지 않습니다. Qwen 3에서는 입력 8192 / 출력 1024 조건에 1.7~6.11배라는 넓은 범위가 보고됐는데, 범위가 이렇게 넓다는 것 자체가 "조건이 전부"라는 증거입니다.
그리고 NVIDIA는 정직한 각주를 하나 답니다. 이론적 "rate-matched" 분석과 실제 종단 벤치마크 사이에 0~25퍼센트의 격차가 있고, 그 원인은 "이상화된 가정"과 "KV 캐시 전송 오버헤드"라는 것입니다. 이론 배수를 그대로 믿지 말라는 이야기를 벤더가 직접 하고 있는 셈입니다.
llm-d on AWS (AWS 자체 측정, 2026년 3월 16일). GPT-OSS 모델, ml.p6-b200.48xlarge 인스턴스, 프리필 파드 4개(TP1)와 디코드 파드 1개(TP4), 입출력 각 1024토큰, 동시성 최대 128. 결과는 "동시성이 올라갈수록 표준 vLLM 배포 대비 초당 토큰 수가 최대 70퍼센트까지 증가". 여기서도 "동시성이 올라갈수록"이라는 조건이 붙어 있습니다 — 부하가 낮으면 이득이 없다는 뜻입니다. 그리고 AWS 저자들은 이렇게 덧붙입니다. "하지만 모든 워크로드를 위한 것은 아닙니다. 더 큰 모델, 더 긴 입력 시퀀스, 희소 MoE 아키텍처에서 시도해 보십시오."
패턴이 보이시나요. 모든 숫자에 지연 목표 또는 부하 조건이 고정돼 있습니다. 이건 우연이 아닙니다. 그게 PD 분리가 파는 상품이기 때문입니다.
KV를 옮기는 배관 — 진짜 엔지니어링은 여기 있다
PD 분리의 아이디어는 한 문단이면 설명됩니다. 어려운 건 배관입니다. 프리필 GPU가 만든 KV 캐시를 디코드 GPU로 옮겨야 하는데, 이게 작지 않습니다. 그리고 이 전송이 TTFT에 그대로 얹힙니다.
그래서 2026년의 실제 경쟁은 전송 계층에서 벌어집니다.
NIXL(NVIDIA Inference Xfer Library)이 현재 가장 널리 쓰이는 축입니다. 저장소의 설명 그대로는 "NVIDIA Dynamo 같은 AI 추론 프레임워크의 점대점 통신을 가속하기 위한 것이며, 모듈식 플러그인 아키텍처를 통해 다양한 메모리(CPU·GPU)와 스토리지(파일·블록·오브젝트 스토어)에 대한 추상화를 제공"합니다. Apache 2.0이고, 백엔드 플러그인이 UCX, POSIX, OBJ, AZURE_BLOB, HF3FS, MOONCAKE, GUSLI, UCCL, GDS, GPUNETIO, LIBFABRIC 등으로 붙습니다. 요컨대 KV 캐시를 InfiniBand로도, NVMe로도, S3로도 옮길 수 있게 한 추상화 계층입니다.
Mooncake는 SGLang의 기본 전송 엔진이고, SGLang은 Mooncake·NIXL·ASCEND 세 백엔드를 지원합니다. Mooncake 경로는 --disaggregation-ib-device로 InfiniBand 장치를 지정해야 합니다.
vLLM 쪽 커넥터 목록은 지금 이렇습니다 — NixlConnector, LMCacheConnectorV1, MooncakeConnector, MoRIIOConnector(ROCm 전용), MultiConnector, OffloadingConnector, FlexKVConnectorV1, 그리고 예제용 ExampleConnector. 내부 추상화는 세 조각입니다 — 커넥터(생산자·소비자 간 KV 검색), LookupBuffer(insert는 논블로킹, drop_select는 블로킹), Pipe(단방향 FIFO).
여기서 배관이 요구사항으로 되돌아온다는 점을 놓치면 안 됩니다. RDMA 패브릭이 사실상 전제입니다. AWS가 llm-d를 굴리며 EFA와 libfabric 플러그인을 함께 이야기하는 이유가 이것이고, SGLang이 InfiniBand 장치 지정을 요구하는 이유도 같습니다. 평범한 이더넷 위에서 이 설계를 얹으면, 아껴 놓은 ITL을 전송 지연으로 도로 토해 냅니다.
2026년 4월, MORI-IO — 구체적인 측정 하나
추상적인 이야기를 구체적인 측정 하나로 내려 봅시다. 2026년 4월 7일 vLLM 블로그에 MORI-IO 커넥터 글이 올라왔습니다. AMD와 Embedded LLM 팀이 쓴 글이고, 측정은 2026년 3월 12일에 수행됐습니다. AMD 자체 측정이라는 점을 먼저 밝혀 둡니다.
MORI-IO는 MORI(Modular RDMA Interface) 프레임워크 위에 올린 RDMA 기반 KV 캐시 커넥터로, vLLM에 기여됐습니다. AMD GPU(ROCm) 진영에서 NIXL에 해당하는 자리를 채우는 물건이라고 보면 됩니다.
측정 조건은 이렇습니다.
- 하드웨어: AMD Instinct MI300X 8장(각 192GB), AMD EPYC 9654 96코어 2소켓
- 소프트웨어: Ubuntu 22.04 LTS, ROCm 7.0.51831, vLLM 0.16.0rc1.dev1
- 모델:
Qwen/Qwen3-235B-A22B-FP8 - 워크로드: 입력 2000토큰, 출력 1000토큰, 총 100개 요청, 요청률 0.5~10 req/s
- SLO: TTFT 1초 미만, ITL 토큰당 50ms 미만
- 구성: 프리필 GPU 0-3, 디코드 GPU 4-7, 그리고 프록시 서버
요청률 8 req/s 지점에서 보고된 결과입니다.
| 구성 | SLO 만족 요청 | 상대 goodput |
|---|---|---|
| 표준 (1× TP8) | 26/100 | 0.9배 |
| 표준 (2× TP4) | 30/100 | 1배 (기준) |
| MORI-IO Read (1P+1D) | 70/100 | 2.4배 |
| MORI-IO Write (1P+1D) | 73/100 | 2.5배 |
이 표에서 제일 중요한 건 배수가 아니라 왜 실패했는지입니다. 블로그의 문장을 그대로 옮깁니다.
"두 분리 모드 모두 ITL 위반을 완전히 제거한다. 남은 실패는 요청률이 오르면서 발생하는 TTFT 초과다."
이게 PD 분리의 정체를 한 문장으로 요약합니다. ITL 위반이 0이 됐습니다. 디코드 GPU 위에는 프리필이 끼어들 수 없으니 당연합니다. 그런데 실패가 사라진 게 아니라 TTFT 쪽으로 옮겨 갔습니다. 병목을 없앤 게 아니라 이동시킨 것이고, 그 거래가 남는 장사였을 뿐입니다.
Read 모드와 Write 모드의 차이도 같은 이야기의 연장입니다.
- Read 모드: 디코드가 프리필에서 KV를 RDMA로 당겨 온다. 블로그의 설명으로는 "TTFT가 최소 한 번의 완전한 프리필 포워드 패스(프록시 직렬화)에 RDMA 전송 시간을 더한 만큼 증가"한다.
- Write 모드: 프리필이 연산 중에 KV를 디코드로 밀어 넣는다. "TTFT는 RDMA 전송 시간만큼만 증가하는데, 이 전송이 프리필 연산과 겹치므로 순 페널티가 더 작다."
그래서 Write가 73, Read가 70입니다. 블로그의 설명대로 "동시 프록시 디스패치가 TTFT를 낮추기" 때문입니다. 3점 차이의 정체가 결국 TTFT 페널티의 크기라는 뜻입니다.
그리고 각주 하나가 조용히 붙어 있습니다. "MORI-IO 커넥터가 요구하는 대로 프리픽스 캐싱 비활성화." 두 분리 구성 모두에서 그렇습니다. 이건 작은 각주가 아닙니다 — 다음 절에서 다루겠습니다.
공짜가 아니다 — 정직한 비용
이제 청구서입니다.
TTFT가 나빠진다. 앞에서 본 그대로입니다. KV 캐시가 네트워크를 건너는 시간이 TTFT에 얹히고, Read 모드에서는 프록시 직렬화까지 더해집니다. MORI-IO 측정에서 SLO를 못 지킨 27~30개 요청이 전부 TTFT 초과였다는 사실을 기억하십시오. PD 분리는 ITL 문제를 TTFT 문제로 바꾸는 거래입니다. 당신의 병목이 TTFT라면 이 거래는 손해입니다.
프리픽스 캐싱과 충돌할 수 있다. MORI-IO 측정에서 프리픽스 캐싱은 커넥터 요구사항으로 꺼져 있었습니다. 이게 왜 아픈가 하면 — 프리픽스 캐싱은 긴 시스템 프롬프트나 멀티턴 대화에서 프리필 비용을 통째로 날려 주는, 대부분의 실무 워크로드에서 가장 큰 단일 최적화이기 때문입니다. 긴 공통 프리픽스를 공유하는 챗봇 트래픽이라면, 프리픽스 캐싱을 끄고 얻은 2.5배 goodput이 프리픽스 캐싱을 켠 통합 서빙보다 나은지는 전혀 자명하지 않습니다. 이건 반드시 당신의 트래픽으로 직접 재야 하는 항목입니다. (커넥터와 버전에 따라 상황이 다르므로, 쓰려는 조합의 현재 제약을 확인하십시오.)
RDMA 패브릭이 필요하다. InfiniBand나 RoCE, AWS라면 EFA. 이건 하드웨어 조달과 네트워크 설계의 문제이지 플래그 하나 켜는 문제가 아닙니다.
GPU가 여러 장 필요하다. 최소 프리필 1 + 디코드 1이고, 실전 구성은 그보다 큽니다 — MORI-IO 측정은 8장을 4:4로 갈랐고, AWS 사례는 프리필 파드 4개와 디코드 파드 1개였습니다. GPU 두어 장으로 굴리는 서비스에서 논의할 주제가 아닙니다.
운영 복잡도가 늘어난다. 인스턴스 하나가 인스턴스 두 종류 + 프록시/라우터 + 전송 계층이 됩니다. 프리필과 디코드의 비율을 워크로드에 맞춰 튜닝해야 하고(AWS 글이 지적하는 그 항목입니다), 장애 모드도 그만큼 늘어납니다. 프로파일링조차 번거로워집니다 — SGLang 문서는 프리필 워커와 디코드 워커를 따로 프로파일해야 한다고 적어 두었습니다.
모델 아키텍처를 탄다. SGLang의 스테이징 버퍼 최적화는 비-MLA 모델(GQA·MHA)용이고, 문서는 "MLA 모델(DeepSeek-V2/V3 등)은 이 플래그를 켜면 안 된다"고 명시합니다. TensorRT-LLM은 이 접근이 "긴 입력 시퀀스와 중간 길이 출력" 워크로드에 가장 유리하다고 말합니다. 짧은 프롬프트에 긴 출력이라면 프리필이 애초에 병목이 아니니 나눌 이유도 적습니다.
아직 experimental이다. 적어도 vLLM에서는 그렇습니다.
그래서, 당신은 써야 하나
판단 기준은 의외로 선명합니다. MORI-IO 블로그가 직접 답을 줍니다.
"프로덕션 부하에서 ITL p99이 SLO를 초과한다면: 분리하십시오 — 이것이 주된 사용 사례입니다."
"낮은 요청률에 짧은 프롬프트: 표준 서빙으로 충분합니다."
여기에 지금까지의 재료를 얹어 정리하면 이렇습니다.
써서 값을 하는 경우
- 꼬리 ITL이 실제 SLO 위반의 원인이다. 스트리밍이 끊긴다는 불만이 있고, p99 ITL을 재 보니 프리필 간섭이 범인이다.
- 긴 입력 + 중간 길이 출력 워크로드다. (TensorRT-LLM이 명시한 조건)
- 부하가 실제로 높다. AWS 측정의 이득이 "동시성이 올라갈수록" 나타났다는 걸 기억하십시오.
- RDMA 패브릭과 GPU 여유가 이미 있다.
- 큰 모델, 특히 희소 MoE. (AWS가 권한 조건)
과잉이거나 손해인 경우
- 병목이 TTFT다. 분리는 TTFT를 더 나쁘게 만듭니다.
- 프리픽스 캐싱이 당신의 주력 최적화다. 긴 공통 시스템 프롬프트를 공유하는 챗봇이라면 특히.
- 부하가 낮거나 프롬프트가 짧다.
- GPU가 몇 장 없거나 RDMA가 없다.
- 그냥 처리량이 아쉬운 것뿐이다. — 이 경우엔 분리가 답이 아닙니다. vLLM 문서가 대문자로 말해 준 그대로입니다.
마지막 항목이 제일 흔한 오해입니다. "GPU 비용을 줄이고 싶다"는 동기로 PD 분리를 검토하러 왔다면, 방향이 틀렸습니다. 그건 양자화나 배치 튜닝이 푸는 문제입니다. PD 분리가 파는 건 예측 가능성이지 용량이 아닙니다.
마치며
정리하면 이렇습니다. 프리필은 연산 집약적이고 디코드는 메모리 집약적인데, 한 GPU에 같이 두면 프리필이 디코드를 끊어 먹습니다. PD 분리는 둘을 다른 GPU로 갈라 그 간섭을 원천 제거하고, 각 단계의 병렬화를 따로 튜닝하게 해 줍니다. MORI-IO 측정에서 ITL 위반이 완전히 사라진 게 그 증거입니다.
대신 KV 캐시를 네트워크로 옮겨야 하고, 그 시간이 TTFT에 얹히고, RDMA 패브릭과 여러 장의 GPU와 늘어난 운영 복잡도를 요구하고, 프리픽스 캐싱 같은 기존 최적화와 부딪힐 수 있습니다. 그리고 순수 처리량은 — vLLM 문서의 표현대로 — 늘어나지 않습니다.
그래서 PD 분리는 "더 빠른 서빙"이 아니라 "약속을 지키는 서빙"입니다. 같은 GPU로 더 많이 뱉는 기술이 아니라, 뱉는 것들이 SLO 안에 들어오게 만드는 기술입니다. 이 구분을 흐리는 순간 인터넷의 배수 숫자에 속게 됩니다 — 그 숫자들은 전부 지연 목표를 고정한 채 잰 goodput이니까요.
그러니 순서는 이렇습니다. 먼저 p99 ITL을 재십시오. 그게 SLO를 넘고 있고 범인이 프리필 간섭이라면, 그때 분리를 꺼내십시오. 재 보지도 않고 분리부터 하는 건, 있지도 않은 병을 아주 비싼 약으로 치료하는 일입니다.
참고 자료
- Disaggregated Prefilling (experimental) — vLLM 공식 문서 — 커넥터 목록과 "처리량을 개선하지 않는다"는 문장의 출처
- Next-Level Inference: Why Your Single-Node vLLM Setup Needs Prefill-Decode Disaggregation — vLLM 블로그, 2026-04-07 — AMD·Embedded LLM의 MORI-IO 커넥터와 MI300X 측정(벤더 자체 측정)
- DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM Serving (Zhong 등, OSDI 2024, arXiv 2401.09670) — goodput 개념의 출처
- Disaggregated Serving in TensorRT-LLM — NVIDIA 기술 블로그 — GB200 측정과 rate-matching 격차(벤더 자체 측정)
- Introducing Disaggregated Inference on AWS powered by llm-d — AWS, 2026-03-16 — EFA·NIXL 기반 llm-d 측정(벤더 자체 측정)
- PD Disaggregation — SGLang 공식 문서 — 프리필 인터럽션·DP 어텐션 불균형, 전송 엔진과 MLA 제약
- ai-dynamo/nixl — NVIDIA Inference Xfer Library — 전송 계층 추상화와 백엔드 플러그인
- KV 캐시를 4비트로 — SAW-INT4와 시스템을 아는 INT4 양자화(관련 글)
PD Disaggregation Doesn't Increase Throughput — What Prefill/Decode Disaggregation Actually Buys You
- Introduction — Two Jobs With Opposite Personalities, Living on One GPU
- Why Putting Both on One GPU Is a Problem — Interference
- So You Disaggregate — The Structure of PD Disaggregation
- How to Read the Numbers — Goodput, Not Throughput
- Taking the Multipliers Apart
- The Plumbing That Moves KV — The Real Engineering Is Here
- April 2026, MORI-IO — One Concrete Measurement
- It's Not Free — The Honest Costs
- So, Should You Use It?
- Closing
- References
Introduction — Two Jobs With Opposite Personalities, Living on One GPU
It's common knowledge by now that LLM inference splits into two stages: prefill, which pushes the entire prompt through a single forward pass to build the KV cache, and decode, which pulls out tokens one at a time. These are the same two stages that come up every time we talk about the KV cache.
What matters is that the two have opposite personalities. In the SGLang documentation's own words, prefill is a computation-intensive job that processes the entire input sequence, while decode is a memory-intensive job that manages the KV cache. Prefill burns the GPU's compute units, while decode is bound by memory bandwidth because it has to re-read the entire model weights and KV cache for every single token.
What happens when you put two jobs with opposite personalities on the same GPU and run them under the same scheduler? And does splitting them really make things better? This post is about how far that "split" — prefill/decode disaggregation (PD disaggregation) — has come as of 2026, and why you shouldn't take the multiplier numbers floating around the internet at face value.
Why Putting Both on One GPU Is a Problem — Interference
The SGLang documentation pins down exactly two problems that arise in a unified engine.
First, Prefill Interruption. In the documentation's own words, "incoming prefill batches frequently interrupt ongoing decode batches, causing significant delays in token generation." Picture it and it's obvious: a user is streaming tokens one by one, and meanwhile an 8,000-token prompt arrives from someone else. That prefill occupies the entire GPU for a while, and the streaming user's next token comes out that much later. From the user's side, it's the moment the text flow suddenly stops.
Second, DP Attention Imbalance. In data-parallel attention, if one DP worker processes a prefill batch while another simultaneously processes a decode batch, decode latency grows.
NVIDIA's TensorRT-LLM technical blog makes the same diagnosis — in unified serving, "context processing delays token generation," producing interference that degrades interactivity. And the blog sums up the essence of the problem in one line: "optimizing for one metric, like TTFT, frequently comes at the cost of another, like TPOT."
This is the crux. On a single GPU, prefill and decode are two conflicting goals competing for the same resource. Grow the batch and throughput rises but tail latency falls apart; slice prefill into small pieces (chunked prefill) and interference drops but prefill efficiency suffers. Trying to satisfy both goals with a single scheduler is structurally a compromise, full stop.
So You Disaggregate — The Structure of PD Disaggregation
The idea is simple: since the two have different personalities, give them separate hardware.
By the vLLM documentation's definition, disaggregated prefill runs the prefill and decode stages on separate vLLM instances and moves the KV cache between them via a connector. An actual deployment looks roughly like this.
[ Proxy / Router ]
| |
Request --->| |
v v
[ Prefill Instance ] [ Decode Instance ]
GPU 0-3 GPU 4-7
Prompt -> KV KV -> Token stream
| ^
| KV Cache |
+--- RDMA --+
(Connector)
You need three pieces.
- Prefill instance — takes the prompt and builds the KV cache.
- Decode instance — takes that KV cache and pulls out tokens.
- Connector and proxy — carries the KV cache across and coordinates which side a request goes to.
Now you can apply a separate parallelization strategy to each stage — low TP for prefill, high TP for decode, or the reverse. This is exactly the first benefit the vLLM documentation lists for this feature: "tuning TTFT and ITL separately." The second is "controlling tail ITL." Since a prefill job can no longer interrupt decode, tokens come out of the decode GPU at a steady rhythm.
In vLLM, this feature is still marked experimental. In the documentation's own words, "this feature is experimental and subject to change."
How to Read the Numbers — Goodput, Not Throughput
This is the heart of the post.
Search for PD disaggregation and numbers like "2x" and "7x" pour out. Yet the official vLLM documentation has this sentence pinned down in capital letters.
"Disaggregated prefill DOES NOT improve throughput."
It looks like a contradiction. But both are true. Once you understand why, it changes how you see PD disaggregation completely.
Think about it and it's obvious. PD disaggregation doesn't conjure up more GPUs. Splitting 8 GPUs into 4 for prefill and 4 for decode can't possibly increase total compute. If anything, moving the KV cache over the network is new work, so in terms of pure total work, it actually increases. The TensorRT-LLM blog admits as much — "disaggregation incurs the overhead of transferring KV cache blocks."
So what are those multipliers actually measuring? The answer is goodput.
The concept traces back to DistServe (Zhong et al., OSDI 2024), the original PD disaggregation paper — the title itself says "Goodput-optimized." Goodput is defined roughly as the maximum request rate the system can sustain while satisfying both TTFT and TPOT constraints. Where plain throughput is "how many did it spit out per second," goodput is "how many did it spit out per second at the quality it promised."
How big this difference is comes through clearly in the MORI-IO connector blog post that landed in vLLM. That post defines goodput this way.
"Goodput = maximum request rate (req/s) such that requests satisfy both TTFT < T_ttft and ITL < T_itl."
In other words, a request that misses its SLO doesn't get counted. In a throughput calculation, a TTFT that took 3 seconds still counts as one request; in a goodput calculation, it counts as zero — because the user has probably already closed the tab.
So to sum up:
- Throughput (pure tokens/s, latency ignored): PD disaggregation does not improve it. If anything, transfer overhead makes it slightly worse. This is what the vLLM documentation is talking about.
- Goodput (SLO-compliant throughput): PD disaggregation improves it substantially. This is what the vendor blogs are measuring.
Same system, different ruler. Which is exactly why, whenever you see a multiplier on the internet, the first thing to check is which ruler was used.
Taking the Multipliers Apart
Let's re-read the published numbers through this lens. Everything below is a vendor/author self-reported measurement, and I've noted the conditions for each — the conditions are the meaning of the number.
DistServe (Zhong et al., OSDI 2024, author self-reported). The paper's headline figures are "7.4x more requests" or "12.6x tighter SLO." But a condition immediately follows — "while staying within latency constraints for over 90% of requests." This isn't a throughput multiplier; it's a request-rate multiplier with the SLO-attainment rate held fixed. And the paper itself says the multiplier varies by model, application, and latency requirement.
TensorRT-LLM (NVIDIA self-reported, GB200). These numbers are especially instructive. On DeepSeek R1 with 4,400 input / 1,200 output tokens: 1.4-1.8x without MTP, 1.6-2.5x with MTP on. At 8,192 input / 256 output: up to 1.73x on 4 GPUs, 2x on 8 GPUs. And the decisive entry — at 4,096 input / 1,024 output, "1.7x improvement at the 50 tokens/s/user point." Read that last phrase again: it's a throughput multiplier with a latency target of 50 tokens/s per user held fixed. Relax the latency constraint and this multiplier does not stay put. For Qwen 3 at 8,192 input / 1,024 output, a wide range of 1.7-6.11x was reported — and a range that wide is itself evidence that "the conditions are everything."
And NVIDIA appends an honest footnote: there's a 0-25% gap between the theoretical "rate-matched" analysis and actual end-to-end benchmarks, attributed to "idealized assumptions" and "KV cache transfer overhead." In effect, the vendor is telling you, in its own words, not to take the theoretical multiplier at face value.
llm-d on AWS (AWS self-reported, March 16, 2026). GPT-OSS model, ml.p6-b200.48xlarge instance, 4 prefill pods (TP1) and 1 decode pod (TP4), 1,024 tokens each for input and output, concurrency up to 128. The result: "as concurrency increases, tokens per second increase by up to 70% over standard vLLM deployment." Here too the condition "as concurrency increases" is attached — meaning there's no gain at low load. And the AWS authors add: "but it's not for every workload. Try it on larger models, longer input sequences, and sparse MoE architectures."
Notice the pattern. Every single number has a fixed latency target or load condition attached. That's not a coincidence — it's because that's the product PD disaggregation is selling.
The Plumbing That Moves KV — The Real Engineering Is Here
The idea behind PD disaggregation fits in one paragraph. The hard part is the plumbing. The KV cache built by the prefill GPU has to move to the decode GPU, and it isn't small — and this transfer lands directly on top of TTFT.
So the real competition in 2026 is playing out at the transfer layer.
NIXL (NVIDIA Inference Xfer Library) is currently the most widely used axis. In the repository's own description, it's "designed to accelerate point-to-point communication in AI inference frameworks such as NVIDIA Dynamo... while providing a consistent abstraction across diverse memory (CPU, GPU) and storage (file, block, object store) tiers via a modular plug-in architecture." It's Apache 2.0-licensed, with backend plugins for UCX, POSIX, OBJ, AZURE_BLOB, HF3FS, MOONCAKE, GUSLI, UCCL, GDS, GPUNETIO, LIBFABRIC, and more. In short, it's an abstraction layer that lets the KV cache move over InfiniBand, NVMe, or even S3.
Mooncake is SGLang's default transfer engine, and SGLang supports three backends: Mooncake, NIXL, and ASCEND. The Mooncake path requires specifying the InfiniBand device via --disaggregation-ib-device.
On the vLLM side, the current connector list is: NixlConnector, LMCacheConnectorV1, MooncakeConnector, MoRIIOConnector (ROCm-only), MultiConnector, OffloadingConnector, FlexKVConnectorV1, and the example ExampleConnector. The internal abstraction has three pieces — the Connector (KV lookup between producer and consumer), the LookupBuffer (insert is non-blocking, drop_select is blocking), and the Pipe (a unidirectional FIFO).
Don't miss the point where the plumbing turns back into a requirement. An RDMA fabric is effectively a precondition. That's why AWS talks about EFA and the libfabric plugin in the same breath as running llm-d, and why SGLang requires specifying an InfiniBand device. Layer this design on top of plain Ethernet, and you hand back the ITL you saved as transfer latency.
April 2026, MORI-IO — One Concrete Measurement
Let's bring the abstract discussion down to one concrete measurement. On April 7, 2026, the vLLM blog published a post on the MORI-IO connector, written by the AMD and Embedded LLM teams, with the measurement carried out on March 12, 2026. Let me flag up front that this is an AMD self-reported measurement.
MORI-IO is an RDMA-based KV cache connector built on top of the MORI (Modular RDMA Interface) framework and contributed to vLLM. Think of it as filling the role NIXL plays, but on the AMD GPU (ROCm) side.
The measurement conditions:
- Hardware: 8x AMD Instinct MI300X (192GB each), 2x AMD EPYC 9654 96-core sockets
- Software: Ubuntu 22.04 LTS, ROCm 7.0.51831, vLLM 0.16.0rc1.dev1
- Model:
Qwen/Qwen3-235B-A22B-FP8 - Workload: 2,000 input tokens, 1,000 output tokens, 100 total requests, request rate 0.5-10 req/s
- SLO: TTFT under 1 second, ITL under 50ms per token
- Configuration: prefill on GPU 0-3, decode on GPU 4-7, plus a proxy server
Results reported at the 8 req/s request-rate point:
| Configuration | SLO-satisfying requests | Relative goodput |
|---|---|---|
| Standard (1x TP8) | 26/100 | 0.9x |
| Standard (2x TP4) | 30/100 | 1x (baseline) |
| MORI-IO Read (1P+1D) | 70/100 | 2.4x |
| MORI-IO Write (1P+1D) | 73/100 | 2.5x |
The most important thing in this table isn't the multiplier — it's why the requests failed. Quoting the blog post directly:
"Both disaggregated modes completely eliminate ITL violations. The remaining failures are TTFT overruns that occur as the request rate increases."
This sums up the true nature of PD disaggregation in one line. ITL violations went to zero. That's exactly what you'd expect, since prefill can no longer intrude on the decode GPU. But the failures didn't disappear — they moved over to TTFT. The bottleneck wasn't eliminated, it was relocated, and the trade just happened to be a profitable one.
The difference between Read mode and Write mode is an extension of the same story.
- Read mode: decode pulls the KV from prefill over RDMA. Per the blog's description, "TTFT increases by at least one full prefill forward pass (proxy serialization) plus the RDMA transfer time."
- Write mode: prefill pushes the KV to decode while it's still computing. "TTFT increases only by the RDMA transfer time, and because this transfer overlaps with prefill compute, the net penalty is smaller."
That's why Write scores 73 and Read scores 70 — per the blog, because "concurrent proxy dispatch lowers TTFT." In other words, the 3-point gap is ultimately about the size of the TTFT penalty.
And one footnote is quietly attached: "prefix caching disabled, as required by the MORI-IO connector." This holds for both disaggregated configurations. This is not a small footnote — I'll cover it in the next section.
It's Not Free — The Honest Costs
Now for the bill.
TTFT gets worse. Exactly as we saw above. The time it takes the KV cache to cross the network lands on top of TTFT, and Read mode adds proxy serialization on top of that. Remember that in the MORI-IO measurement, all 27-30 requests that missed SLO were TTFT overruns. PD disaggregation is a trade that converts an ITL problem into a TTFT problem. If your bottleneck is TTFT, this trade is a net loss.
It can conflict with prefix caching. In the MORI-IO measurement, prefix caching was turned off as a connector requirement. This hurts because prefix caching, which wipes out the prefill cost entirely for long system prompts or multi-turn conversations, is the single biggest optimization in most real-world workloads. If your traffic is chatbot traffic sharing a long common prefix, whether the 2.5x goodput you get from turning prefix caching off actually beats unified serving with prefix caching on is not obvious at all. This is something you have to measure directly with your own traffic. (This varies by connector and version, so check the current constraints of the combination you intend to use.)
It requires an RDMA fabric. InfiniBand or RoCE — EFA if you're on AWS. This is a hardware-procurement and network-design problem, not a matter of flipping one flag.
It needs multiple GPUs. The bare minimum is 1 for prefill + 1 for decode, and real-world configurations run larger — the MORI-IO measurement split 8 GPUs 4:4, and the AWS case used 4 prefill pods against 1 decode pod. This isn't a topic worth discussing for a service running on a couple of GPUs.
Operational complexity goes up. One instance becomes two instance types plus a proxy/router plus a transfer layer. You have to tune the prefill-to-decode ratio for your workload (this is the exact point the AWS post raises), and failure modes multiply accordingly. Even profiling gets more cumbersome — the SGLang documentation notes that you need to profile prefill workers and decode workers separately.
It's sensitive to model architecture. SGLang's staging-buffer optimization is for non-MLA models (GQA, MHA), and the documentation explicitly states "MLA models (e.g., DeepSeek-V2/V3) should not enable this flag." TensorRT-LLM says this approach benefits "long input sequences and medium-length outputs" workloads the most. With a short prompt and a long output, prefill was never the bottleneck to begin with, so there's less reason to split it out.
It's still experimental. At least in vLLM.
So, Should You Use It?
The decision criterion turns out to be surprisingly clear-cut. The MORI-IO blog gives you the answer directly.
"If ITL p99 exceeds SLO under production load: disaggregate — this is the primary use case."
"Low request rate with short prompts: standard serving is sufficient."
Layering everything covered so far on top of this gives the following.
When it earns its keep
- Tail ITL is the actual cause of SLO violations. You have complaints about streaming stalling, and measuring p99 ITL points the finger at prefill interference.
- It's a long-input, medium-length-output workload. (The condition TensorRT-LLM explicitly names.)
- Load is genuinely high. Remember that the gains in the AWS measurement showed up "as concurrency increases."
- You already have an RDMA fabric and GPU headroom.
- Large models, especially sparse MoE. (The condition AWS recommends.)
When it's overkill or a net loss
- Your bottleneck is TTFT. Disaggregation makes TTFT worse.
- Prefix caching is your primary optimization — especially if you're a chatbot sharing a long common system prompt.
- Load is low, or prompts are short.
- You have few GPUs, or no RDMA.
- You just want more throughput, full stop. In this case disaggregation isn't the answer — exactly what the vLLM documentation says, in capital letters.
That last item is the most common misunderstanding. If you came to evaluate PD disaggregation motivated by "I want to cut GPU cost," you're pointed the wrong direction. That's a problem quantization or batch tuning solves. What PD disaggregation sells is predictability, not capacity.
Closing
To sum up: prefill is compute-intensive and decode is memory-intensive, and putting both on one GPU means prefill cuts decode off. PD disaggregation splits the two onto separate GPUs, eliminating that interference at the source and letting you tune each stage's parallelization independently. The complete disappearance of ITL violations in the MORI-IO measurement is the evidence.
In exchange, you have to move the KV cache over the network, that time lands on TTFT, it demands an RDMA fabric, multiple GPUs, and more operational complexity, and it can collide with existing optimizations like prefix caching. And pure throughput — in the vLLM documentation's own words — does not increase.
So PD disaggregation isn't "faster serving" — it's "serving that keeps its promises." It's not a technique for spitting out more from the same GPUs; it's a technique for making sure what you do spit out lands within SLO. The moment you blur that distinction, you fall for the multiplier numbers on the internet — because every one of them is goodput measured with a latency target held fixed.
So the order of operations is this: measure your p99 ITL first. If it's exceeding SLO and the culprit is prefill interference, that's when you reach for disaggregation. Reaching for disaggregation before you've even measured is treating a disease you don't have with a very expensive drug.
References
- Disaggregated Prefilling (experimental) — vLLM official docs — source of the connector list and the "does not improve throughput" sentence
- Next-Level Inference: Why Your Single-Node vLLM Setup Needs Prefill-Decode Disaggregation — vLLM blog, 2026-04-07 — AMD/Embedded LLM's MORI-IO connector and MI300X measurement (vendor self-reported)
- DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM Serving (Zhong et al., OSDI 2024, arXiv 2401.09670) — source of the goodput concept
- Disaggregated Serving in TensorRT-LLM — NVIDIA technical blog — GB200 measurements and the rate-matching gap (vendor self-reported)
- Introducing Disaggregated Inference on AWS powered by llm-d — AWS, 2026-03-16 — EFA/NIXL-based llm-d measurement (vendor self-reported)
- PD Disaggregation — SGLang official docs — prefill interruption, DP attention imbalance, transfer engines, and MLA constraints
- ai-dynamo/nixl — NVIDIA Inference Xfer Library — transfer-layer abstraction and backend plugins
- Quantizing the KV Cache to 4 Bits — SAW-INT4 and System-Aware INT4 Quantization (related post)