Prefill-Decode Disaggregation
- Prefill (프리필): 입력 시퀀스 전체를 한 번에 처리합니다. 예: "안녕하세요. 저는 AI입니다" (10개 토큰)을 모두 입력받아서 각 위치의 어텐션을 계산합니다.
- Decode (디코드): 한 토큰씩 생성합니다. 방금 생성한 "감" 다음에 "사"를 생성하고, 다시 "합니다"를 생성합니다. 한 스텝당 1개 토큰.
전통적인 시스템은 이 둘을 같은 리소스, 같은 배치 크기로 처리합니다. 하지만 둘의 특성은 완전히 다릅니다. Prefill-Decode Disaggregation은 이 둘을 분리해서 각각에 최적화된 리소스를 할당하는 아키텍처입니다.
왜 분리가 필요한가
Prefill의 특징
- 시퀀스 길이 긺 (예: 4K~32K 토큰)
- 배치 크기 작음 (예: 1~4)
- 연산 강도 높음 (많은 FLOPs)
- 메모리 접근 패턴 규칙적
- 목표: 처리량(throughput)
Decode의 특징
- 시퀀스 길이 짧음 (1개 토큰씩 생성)
- 배치 크기 크기 가능 (예: 64~256)
- 연산 강도 낮음 (매 스텝 1개 토큰)
- 메모리 접근 패턴 불규칙 (캐시 미스)
- 목표: 레이턴시 (빠른 응답)
이 두 특성을 하나의 시스템으로 처리하면, 한쪽에 최적화된 설정이 다른 쪽에는 비효율적입니다.
예: GPU를 "큰 배치, 작은 시퀀스 길이"로 설정하면 prefill에서 GPU가 놀고, "작은 배치, 긴 시퀀스"로 설정하면 decode에서 경합이 생깁니다.
분리된 아키텍처의 구조
기본 패턴
┌─────────────────────────────────────┐
│ Request Queue / Router │
└────────┬────────────────────────────┘
│
┌────┴────┐
│ │
v v
┌─────────┐ ┌─────────┐
│Prefill │ │ Decode │
│Engine │ │ Engine │
│(GPU1~2) │ │(GPU3~8) │
└────┬────┘ └────┬────┘
│ │
└─────┬──────┘
v
Result Pool
Prefill Engine: 큰 배치를 받아서 한 번에 처리합니다. 요청들의 시퀀스를 연결(concatenation) 또는 Dynamic Batching으로 처리합니다. 한 번 계산하면 완료.
Decode Engine: 작은 배치로 지속적으로 실행됩니다. 각 요청이 마지막 토큰에 도달할 때까지 반복합니다.
KV 캐시 전달
핵심은 KV 캐시(Key-Value cache) 관리입니다.
Prefill 후 어텐션 계산에서 나온 K, V를 decode 단계를 위해 저장해야 합니다. 전체 시퀀스(예: 4K 토큰)의 K, V를 메모리에 유지합니다.
분리된 아키텍처에서: 1. Prefill이 KV 캐시를 계산합니다. 2. 이를 공유 메모리(예: NVMe)에 저장합니다 (또는 별도 가속기) 3. Decode 엔진이 필요할 때마다 읽어갑니다.
이 과정에서 메모리 대역폭이 병목이 될 수 있습니다. 따라서 KV 캐시를 압축(quantize)하거나, Paged Attention 같은 기법으로 메모리 효율을 높입니다.
실제 구현: vLLM의 예
vLLM의 "분리된 prefill/decode" 옵션:
llm = LLM(
model="meta-llama/Llama-2-70b-hf",
tensor_parallel_size=2,
gpu_memory_utilization=0.9,
disaggregate_prefill=True # 분리 활성화
)
설정하면: - Prefill은 2개 GPU에서 큰 배치로 실행 - Decode는 나머지 6개 GPU에서 작은 배치로 실행 - 각 엔진이 독립적으로 스케일링
결과: 1.5~2배 처리량 향상 (배포 환경, 혼합 워크로드 기준)
언제 효과적인가
효과적:
- 많은 요청이 동시에 도착할 때 — prefill과 decode가 병렬로 실행되어 GPU 활용률이 높아집니다.
- 요청의 입력 길이가 다양할 때 — 긴 입력(prefill 많음)과 짧은 입력(decode 비중)이 섞여 있으면 균형이 맞춰집니다.
- *메모리 대역폭이 충분할 때* — KV 캐시를 전달할 때 메모리 접근이 많아지므로, 고대역폭 환경(예: H100, H200)에서 장점.
비효과적:
- 요청이 순차적으로 도착할 때 — prefill 동안 decode 엔진이 노는 시간이 많습니다.
- *추론 요청이 매우 적을 때* — GPU를 여러 개로 나누면 오버헤드가 이득을 넘습니다.
- *메모리 대역폭이 제한적일 때* — KV 캐시 전달 비용이 감소 이득을 상쇄합니다.
트레이드오프
장점
- 처리량 향상: 여러 요청을 동시에 다루니 전체 스루풋이 증가합니다.
- 리소스 활용: GPU를 각 역할에 최적화 가능합니다.
단점
- 레이턴시 변동성 증가: Prefill 큐가 길면 decode 시작이 지연됩니다.
- 복잡한 스케줄링: Prefill과 decode의 시작 시점을 어떻게 조율할지 결정해야 합니다.
- KV 캐시 관리 복잡성: 캐시 저장소 결정, 거리(locality), 교체 정책을 별도로 설계합니다.
- 배포 비용: 추가 GPU나 저장소가 필요할 수 있습니다.
언어 모델 서빙 시스템의 진화
분리된 prefill/decode는 대규모 서빙 시스템에서 표준에 가까워지고 있습니다.
NVIDIA의 Triton 추론 서버, SGLang(Stanford), vLLM의 최신 버전 모두 이 아키텍처를 지원합니다. LMDeploy(MMDeploy) 같은 경량 프레임워크도 포함하고 있습니다.
향후 방향:
- 동적 조정: 요청 패턴에 따라 GPU를 자동으로 prefill/decode에 재할당.
- *다중 GPU 최적화*: 여러 노드에 분산된 prefill/decode, 노드 간 KV 캐시 동기화.
- *혼합 정밀도와의 결합*: Prefill은 FP8, decode는 INT4 같이 단계마다 다른 정밀도 사용.
결국 분리는 "한 시스템이 모든 것을 처리하려고 하지 말자"는 관찰에서 출발합니다. 특화된 설계가 더 효율적입니다.