들어가며
LLM 추론 서버는 두 가지 일을 합니다. 하나는 사용자가 보낸 프롬프트를 읽는 일입니다. 다른 하나는 답변 토큰을 하나씩 만들어 내보내는 일입니다. 앞의 일을 prefill이라고 부릅니다. 뒤의 일을 decode라고 부릅니다.
오랫동안 이 두 일은 같은 GPU에서 함께 처리됐습니다. 그런데 2024년에 DistServe라는 연구가 다른 방식을 제안했습니다. prefill과 decode를 서로 다른 GPU로 나누자는 것입니다. 이것을 disaggregation이라고 합니다.
처음에는 이 방식이 크게 주목받지 못했습니다. 분리하려면 서빙 구조를 많이 바꿔야 했기 때문입니다. 하지만 18개월이 지난 지금은 상황이 달라졌습니다. vLLM과 NVIDIA Dynamo를 비롯한 주요 서빙 프레임워크가 모두 이 방식을 채택했습니다.
이 글에서는 두 일을 왜 분리해야 하는지 살펴보겠습니다. 먼저 prefill과 decode가 어떻게 다른지 짚어 보겠습니다. 그다음 둘을 한 GPU에서 함께 돌릴 때 생기는 문제를 살펴보겠습니다.
1. prefill과 decode는 필요한 자원이 다르다
prefill은 프롬프트 전체를 한 번에 읽어 들입니다. 토큰이 수백 개라도 한 번에 처리합니다. 그래서 GPU의 연산 장치를 가득 채워 씁니다. 이렇게 연산이 병목이 되는 상태를 compute-bound라고 합니다.
decode는 한 번에 토큰 하나만 만듭니다. 한 토큰을 만들 때마다 모델 가중치 전체를 메모리에서 다시 읽어야 합니다. 정작 계산량은 많지 않습니다. 그래서 연산 장치는 충분히 쓰이지 않습니다. 대신 메모리에서 데이터를 읽는 속도가 병목이 됩니다. 이렇게 메모리 대역폭이 병목이 되는 상태를 memory-bound라고 합니다.
정리하면 prefill은 연산량이 병목입니다. decode는 메모리 대역폭이 병목입니다. 두 일은 느려지는 이유가 다릅니다. 이 차이가 뒤에 나올 문제의 출발점입니다.
2. 한 GPU에 섞으면 decode가 멈춘다
서버는 보통 여러 요청을 묶어서 한 배치로 처리합니다. 배치로 묶으면 GPU를 더 효율적으로 쓸 수 있기 때문입니다. 그런데 한 배치 안에 prefill 요청과 decode 요청이 섞이면 문제가 생깁니다.
배치는 가장 무거운 작업이 끝날 때까지 함께 기다립니다. prefill은 decode보다 훨씬 무겁습니다. 그래서 decode 요청은 prefill이 끝날 때까지 기다립니다. 이 시간 동안 decode는 새 토큰을 만들지 못합니다.
추론 엔진에는 또 다른 우선순위가 있습니다. 새 요청이 들어오면 먼저 prefill을 처리하려고 합니다. 사용자가 첫 응답을 받기까지 걸리는 시간을 짧게 유지해야 하기 때문입니다. 이 시간을 TTFT(Time To First Token)라고 합니다. 그래서 엔진은 진행 중이던 decode를 잠시 멈추고 새 prefill을 먼저 처리합니다.
decode 요청은 지금까지 만든 토큰들의 중간 계산 결과를 메모리에 들고 있습니다. 이 결과 묶음을 KV 캐시라고 합니다. prefill은 여기에 더해 프롬프트 전체를 메모리에 올려두고 연산합니다. 그래서 새 prefill 요청이 들어오면 GPU 메모리가 빠르게 부족해집니다.
이때 GPU는 진행 중이던 decode 요청의 KV 캐시를 CPU 메모리로 잠시 옮깁니다. 이것을 swapping이라고 합니다. 공간이 더 부족하면 일부 요청은 완전히 제거합니다. 그리고 나중에 다시 계산합니다.
3. 예시로 보는 간섭 문제
요청 세 개가 시간차를 두고 들어오는 상황을 생각해 보겠습니다.
각 요청을 Req0, Req1, Req2라고 부르겠습니다.
아래 그림은 두 방식에서 시간이 어떻게 흘러가는지 비교한 것입니다.
주황색은 prefill입니다.
파란색은 decode입니다.
흰색은 decode가 멈춰 있는 시간입니다.
초록색은 KV 캐시를 옮기는 시간입니다.

위쪽이 colocation입니다. 한 GPU에서 prefill과 decode를 함께 처리합니다. Req1이 도착하면 Req0의 decode가 멈춥니다. Req2가 도착하면 Req0과 Req1의 decode가 함께 멈춥니다. 그래서 흰색 칸이 곳곳에 생깁니다. 이 흰색 칸은 토큰을 만들지 못한 시간입니다.
아래쪽이 disaggregation입니다. decode 워커와 prefill 워커가 따로 있습니다. 새 요청의 prefill은 prefill 워커가 맡습니다. 그래서 decode 워커는 멈추지 않고 토큰을 계속 만듭니다. Req0의 파란색 칸이 끊김 없이 이어지는 것을 볼 수 있습니다.
대신 초록색 칸이 새로 생깁니다. prefill 워커가 만든 KV 캐시를 decode 워커로 옮기는 시간입니다. 분리하면 간섭은 줄어들지만 전송 비용이 새로 생깁니다.
같은 시간 동안 각 요청이 처리한 토큰 수를 세어 보면 차이가 분명합니다.
요청 colocation disaggregation
| Req0 | 9 토큰 | 22 토큰 |
| Req1 | 4 토큰 | 12 토큰 |
| Req2 | 3 토큰 | 6 토큰 |
세 요청 모두 disaggregation에서 더 많은 토큰을 처리했습니다. 특히 가장 먼저 들어온 Req0의 차이가 큽니다. colocation에서는 뒤따라 들어온 요청의 prefill이 우선 처리됩니다. 그래서 Req0의 decode가 자주 멈춥니다.
4. 분리하면 무엇이 달라지는가
prefill과 decode를 서로 다른 GPU로 나누면 이런 방해가 사라집니다. prefill 전용 GPU는 prefill만 처리합니다. decode 전용 GPU는 decode만 처리합니다. 두 작업이 한 배치에서 섞이지 않습니다. 그래서 decode 요청이 새 prefill 때문에 멈추는 일이 없어집니다.
대신 한 가지 일이 새로 필요합니다. prefill GPU가 만든 KV 캐시를 decode GPU로 옮겨야 합니다. 이 전송이 느리면 분리로 얻은 이득이 줄어듭니다. 그래서 prefill과 decode를 분리한 뒤에는 KV 캐시 전송을 어떻게 줄일지가 핵심 과제가 됩니다.
5. disaggregation은 왜 표준이 되었는가
disaggregation을 처음 본격적으로 제안한 연구가 DistServe입니다. 2024년에 나온 논문입니다. 이 논문은 prefill과 decode를 서로 다른 GPU 그룹에서 처리하자고 주장했습니다.
당시 반응은 회의적이었습니다. 한 GPU에서 작동하던 서빙 구조를 둘로 나누려면 공수가 많이 듭니다. KV 캐시를 GPU 사이로 옮기는 경로도 새로 만들어야 합니다. 이런 비용에 비해 이득이 분명하지 않다고 보는 시각이 많았습니다.
시간이 지나며 평가가 바뀌었습니다. 사용자가 첫 토큰을 받기까지 걸리는 시간과 그다음 토큰이 이어지는 속도는 서로 다른 목표입니다. 앞의 목표를 TTFT라고 합니다. 뒤의 목표를 TPOT(Time Per Output Token)라고 합니다. prefill과 decode를 한 GPU에 함께 두면 이 두 목표를 동시에 맞추기 어렵습니다. 분리하면 두 목표를 따로 관리할 수 있습니다.
18개월이 지난 지금은 분리가 표준에 가깝습니다. vLLM과 NVIDIA Dynamo, llm-d, SGLang 같은 주요 프레임워크가 모두 prefill과 decode 분리를 지원합니다.
6. 지금은 무엇이 연구되고 있는가
이제 분리할지 말지는 더 이상 핵심 쟁점이 아닙니다. 질문은 어떻게 더 잘 분리하느냐로 옮겨갔습니다. 크게 세 방향이 연구되고 있습니다.
첫째는 이기종 GPU 배치입니다. prefill과 decode는 필요한 자원이 다릅니다. 그래서 두 작업에 서로 다른 종류의 GPU를 배정하는 것이 유리할 수 있습니다. 어떤 GPU를 얼마나 배정할지가 과제입니다.
둘째는 KV 캐시 효율입니다. 여기에는 두 가지 측면이 있습니다. 하나는 KV 캐시를 어디에 어떻게 저장하느냐입니다. 다른 하나는 KV 캐시를 GPU 사이로 얼마나 빠르게 옮기느냐입니다.
셋째는 네트워크 오버헤드입니다. 분리하면 KV 캐시가 GPU와 GPU 사이를 오갑니다. 이 이동이 느리면 분리로 얻은 이득이 줄어듭니다. 그래서 전송을 빠르게 만드는 방법이 중요해집니다.
이 세 과제를 실제로 풀어내는 것이 오케스트레이션 레이어입니다. 다음 편에서 NVIDIA Dynamo와 llm-d를 예로 들어 살펴보겠습니다.
정리
한 GPU에 섞을 때의 문제 원인
| decode가 멈춘다 | 무거운 prefill과 같은 배치에서 함께 기다린다 |
| decode가 자주 중단된다 | TTFT를 줄이기 위해 새 prefill을 먼저 처리한다 |
| KV 캐시를 옮기거나 다시 계산한다 | prefill이 GPU 메모리를 추가로 점유한다 |
prefill과 decode는 부족한 자원이 서로 다릅니다. 그래서 한 GPU에 함께 두면 서로를 방해합니다. 분리하면 이 방해가 사라집니다. 대신 KV 캐시를 GPU 사이로 옮기는 일이 새로 필요해집니다. 이 전송을 잘 해내는 것이 다음 편의 주제입니다.
출처
- Zhong et al., "DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving", OSDI 2024
- Hao AI Lab, "Disaggregated Inference: 18 Months Later" — https://haoailab.com/blogs/distserve-retro/
'개발지식 > AI' 카테고리의 다른 글
| 단일 GPU로 120B 모델 학습하는 법 - MegaTrain (0) | 2026.06.11 |
|---|---|
| LLM 서빙 prefill·decode 분리 (2) - 오케스트레이션 레이어 (0) | 2026.06.11 |
| LLM 서빙의 메모리 문제와 PagedAttention (2) - PagedAttention과 vLLM (0) | 2026.05.12 |
| LLM 서빙의 메모리 문제와 PagedAttention (1) - KV 캐시와 단편화 (0) | 2026.05.12 |
| 트랜스포머 쉽게 이해하기 (2) - 디코더 (0) | 2026.04.20 |