같은 GPU에서 프리필과 디코드를 함께 돌리면 둘이 서로를 방해한다. 프리필이 들어올 때마다 디코드 스텝이 길어지고, 디코드를 보호하려고 프리필을 미루면 TTFT가 늘어난다. 청크 프리필은 이 방해를 줄이지만 없애지는 못한다. 분리 서빙(disaggregated serving)은 아예 두 단계를 다른 GPU에서 실행한다. 프리필을 맡는 인스턴스가 프롬프트를 처리해 KV 캐시를 만들고 그 KV 캐시를 디코드 인스턴스로 넘겨 생성을 이어 가게 한다.
분리로 얻는 것
DistServe(OSDI 2024)와 Splitwise가 이 구조를 제안했고, Mooncake(FAST 2025)는 프리필 클러스터와 디코드 클러스터를 나누고 KV 캐시를 중심에 둔 구조로 실제 챗봇 서비스를 운영한 사례를 보고했다. 이 연구들이 꼽는 이득은 세 가지다.
방해가 사라진다. 디코드 인스턴스에는 프리필이 끼어들지 않으므로 스텝 시간이 고르고 TPOT의 꼬리가 짧다. 프리필 인스턴스는 디코드를 신경 쓰지 않고 프롬프트를 큰 묶음으로 처리할 수 있다.
단계마다 따로 설정한다. 프리필은 연산 성능에, 디코드는 대역폭과 KV 메모리에 묶이므로 맞는 설정이 다르다. 분리하면 프리필 쪽은 TTFT 목표에 맞춰 병렬 방식과 배치 크기를 고르고, 디코드 쪽은 TPOT 목표와 KV 수용량에 맞춰 따로 고를 수 있다. DistServe는 이렇게 단계별로 자원과 병렬 방식을 따로 정해 굿풋을 높이는 것을 목표로 삼는다.
단계마다 다른 장비를 쓴다. Splitwise는 토큰 생성 단계에는 최신 GPU의 연산 성능이 필요하지 않다고 지적한다. 연산 성능이 높은 GPU는 프리필에, 대역폭과 메모리 대비 가격이 좋은 GPU는 디코드에 배치하는 구성이 가능해진다.
KV 캐시 전송 비용
분리의 대가는 프리필이 만든 KV 캐시를 디코드 인스턴스로 옮기는 일이다. 옮겨야 하는 양은 프롬프트 길이 × 토큰당 KV 크기다.
| 모델 | 토큰당 KV (BF16) | 2,000토큰 프롬프트의 KV |
|---|---|---|
| Llama 3.1 8B | 128KiB | 약 262MB |
| Llama 3.1 70B | 320KiB | 약 655MB |
두 인스턴스가 같은 서버 안에 있으면 NVLink(H100 SXM에서 초당 900GB)로 1ms 안쪽에 옮긴다. 서버가 다르면 네트워크 대역폭이 시간을 정한다. 초당 50GB를 쓸 수 있는 연결이라면 70B 모델의 2,000토큰 KV를 옮기는 데 약 13ms가 걸린다.
655 × 10⁶ 바이트 ÷ 50 × 10⁹ 바이트/s ≈ 13ms
같은 프롬프트의 프리필 계산은 70B 모델에서 약 2.8 × 10¹⁴ FLOP으로, H100 한 장의 BF16 밀집 성능으로 약 286ms, 네 장에 나누면 이상적으로 약 71ms다. 전송 시간은 프리필 시간보다 한 자릿수 작다. 전송은 TTFT에 더해지지만 대부분의 경우 프리필과 디코드의 방해를 없앤 이득보다 작다.
전송을 줄이는 방법
전송이 문제가 되는 것은 네트워크가 느리거나, 프롬프트가 아주 길거나, 요청이 많아 네트워크가 포화될 때다. 줄이는 방법은 몇 가지가 있다.
- 계산과 겹친다. 층별로 계산이 끝나는 대로 그 층의 KV를 보내면 전송 시간 대부분이 남은 층의 계산 뒤에 숨는다
- 가까이 둔다. 같은 요청을 주고받는 프리필 인스턴스와 디코드 인스턴스를 같은 서버나 같은 랙에 배치해 빠른 연결을 쓴다
- 작게 만든다. KV 캐시를 FP8로 저장하면 전송량도 절반이 된다
- 다시 보내지 않는다. 디코드 쪽이나 공유 저장소에 이미 같은 앞부분의 KV가 있으면 그 부분은 보내지 않는다. Mooncake가 KV 캐시를 구조의 중심에 두는 것도 이 재사용 때문이다
두 묶음의 비율
분리하면 프리필 인스턴스와 디코드 인스턴스를 몇 대씩 둘지 정해야 한다. 이 비율은 부하의 모양이 정한다.
- 프롬프트가 길고 출력이 짧은 부하(문서 요약, 검색 증강 질의응답)는 프리필이 무겁다. 프리필 인스턴스가 더 필요하다
- 프롬프트가 짧고 출력이 긴 부하(대화, 글쓰기)는 디코드가 무겁다. 디코드 인스턴스가 더 필요하다
부하의 모양이 하루 중에도 바뀌면 고정 비율은 한쪽을 놀리고 다른 쪽을 막히게 한다. 한쪽에 대기열이 쌓이는데 다른 쪽은 남는 상황을 피하려면 두 묶음의 대기열 길이와 사용률을 따로 보고 인스턴스 역할을 바꾸거나 따로 확장하는 장치가 필요하다. 함께 돌리는 방식에서는 스케줄러가 스텝마다 자동으로 하던 일을 분리 구성에서는 인스턴스 단위로 해야 한다.
분리가 맞는 부하
분리 서빙은 구성 요소가 늘어나는 만큼 운영이 복잡하다. KV 전송 경로, 두 종류의 인스턴스, 둘 사이의 라우팅과 장애 처리가 생긴다. 그래서 이득이 그 복잡함을 넘는 경우에 쓴다.
| 맞는 경우 | 맞지 않는 경우 |
|---|---|
| TTFT와 TPOT 목표가 둘 다 빡빡해서, 함께 돌리면 청크 프리필로도 둘을 동시에 지킬 수 없다 | 지연 목표가 느슨하거나 처리량만 중요한 오프라인 작업 |
| 프롬프트가 길어 프리필 방해가 TPOT 꼬리를 정한다 | 프롬프트가 짧아 프리필 비용이 작다 |
| GPU가 수십 장 이상이라 두 묶음을 나눠도 각각 충분히 크다 | GPU가 몇 장뿐이라 나누면 한쪽이 한두 장이 된다 |
| 고속 네트워크가 있다 | 서버 간 연결이 느리다 |
규모가 작다면 같은 GPU에서 청크 프리필과 우선순위 스케줄링으로 시작하고, 부하 테스트에서 TPOT 꼬리가 프리필 방해 때문에 목표를 넘는다는 것이 확인될 때 분리를 검토한다. 분리 구성도 결국 같은 부하로 잰 GPU당 굿풋으로 함께 돌리는 구성과 비교한다.