LLM 서빙의 병목은 배치에 있다
Static batching의 67% GPU 낭비부터 Prefill-Decode 분리까지, LLM 추론 처리량을 3-5배 끌어올리는 배치 전략의 진화를 추적한다.
LLM 서빙 시스템의 처리량을 3배 높이면서 P99 latency를 동시에 절반으로 줄이는 것이 가능할까? 직관에 반하는 이 결과는 “배치”라는 개념을 어디서 정의하느냐에서 온다. 왜 static batching은 GPU 시간의 67%를 낭비하고, continuous batching은 그 낭비를 0으로 만드는가?
Static Batching의 구조적 낭비
전통적인 static batching은 개 요청을 모아 같은 수의 iteration을 돌린다. 모든 샘플이 batch 내 최장 길이 까지 padding되어 처리된다.
ShareGPT처럼 생성 길이가 heavy-tail 분포를 따르는 실제 워크로드에서는 이 값이 얼마나 작아지는가?
Sequence 길이 가 tail index 의 Pareto 분포를 따를 때, batch size 에서:
Pareto 분포에서 개 최댓값의 기댓값은 이다. , 를 대입하면:
따라서 GPU 시간의 약 69%가 낭비된다.
padding 오버헤드는 compute 낭비로도 직결된다. 이면 attention GEMM FLOPs가 이상적 경우 대비 200% 초과한다. 배치 크기를 3배 늘려도 utilization은 그대로다 — tail latency가 에 지배되는 구조 자체를 바꾸지 않는 한.
Dynamic Batching의 부분적 개선
Dynamic batching은 고정된 시간 창 동안 도착한 요청을 모아 배치를 구성한다. Poisson arrival rate 에서:
를 크게 하면 throughput이 선형으로 증가하지만 latency도 선형으로 증가한다. SLO budget 안에서 최적값을 찾는 것이 전부다.
Dynamic batching은 partial batch 문제를 개선하지만, batch 내에서 여전히 가 모든 샘플의 latency를 지배한다. Utilization은 static과 거의 같은 수준에 머문다.
Continuous Batching — 배치 개념의 폐기
Orca (Yu et al., 2022)의 핵심 통찰은 간단하다: “batch”를 request 단위가 아니라 iteration 단위로 정의한다. 매 iteration(토큰 1개 생성 1 step)마다 active batch를 재구성한다.
Iteration 1: Active = {Req1(T=50), Req2(T=30), Req3(T=100)}
Iteration 2: Req2 완료 → 제거, Req4 도착 → 즉시 prefill 후 추가
Active = {Req1, Req3, Req4}
Iteration 3: Req1 완료 → 제거
Active = {Req3, Req4}
...
이 구조에서 각 요청은 정확히 iteration만 처리된다. padding이 0이다.
, 이면 compute를 67% 절감한다. P99 latency도 바뀐다. Static에서는 — batch 내 최대에 의존했다. Continuous batching에서는:
각 샘플이 자신의 길이만큼만 처리된다. Orca 논문 실측치: throughput 3.0×, P99 latency 4.7× 감소. throughput과 latency가 동시에 개선되는 이유는 “비효율의 제거”이기 때문이다 — trade-off가 아니라 낭비 회수다.
Chunked Prefill — Long Prompt의 TPOT 급등 문제
Continuous batching도 새로운 문제를 드러낸다. 16k 토큰 long prompt의 prefill이 한 iteration을 점유하면, decode 중인 다른 요청들이 그 iteration 동안 완전히 블록된다. TPOT(Time Per Output Token) spike가 발생한다.
Sarathi-Serve(Agrawal, 2023)의 해결은 prefill을 chunk size 로 분할하는 것이다.
| Chunk Size | Num Chunks (L=16k) | TTFT (ms) | TPOT std |
|---|---|---|---|
| 256 | 64 | 640 | 0 |
| 512 | 32 | 320 | 0 |
| 1024 | 16 | 160 | 0 |
| Monolithic | 1 | 160 | 19ms |
chunk size를 줄이면 TTFT가 증가하고 TPOT가 안정된다. SLO budget에 맞춰 최대 chunk size를 선택하는 것이 최적 전략이다:
Prefill-Decode 분리 — Hardware Optimal의 차이
연속 배치의 궁극적 진화는 prefill과 decode를 다른 GPU 클러스터에서 처리하는 것이다.
Roofline model에서 두 단계의 arithmetic intensity는 본질적으로 다르다. Prefill은 (compute-bound), decode는 (memory-bound)이다. A100의 ridge point( FLOPS/byte)를 기준으로 prefill은 항상 compute-bound 구간에, decode는 항상 memory-bound 구간에 있다.
같은 GPU에서 두 단계를 섞으면 한쪽이 항상 idle이다. DistServe(Zhong 2024)와 Splitwise(Patel 2024)는 이를 분리한다.
분리의 비용은 KV cache 전송이다. LLaMA-70B, GQA(), prompt 2048 토큰 기준으로 RDMA(100 Gbps)를 통한 KV 전송 시간은 약 1.67ms다. NVLink(600 Gbps)에서는 0.28ms. 이 전송 비용이 분리로 얻는 utilization 개선보다 작아야 disaggregation이 이득이다.
고트래픽(100+ req/s) 환경에서는 disaggregation이 비용 대비 처리량 관점에서 압도적으로 유리하다. Splitwise 실측치: 동일 비용 대비 goodput 3×.
정리
- Static batching의 GPU 낭비는 구조적이다. Heavy-tail 분포에서 utilization은 이론적으로 이 상한이며, batch size를 늘려도 해결되지 않는다.
- Continuous batching은 “batch” 개념을 iteration 단위로 재정의해 padding을 0으로 만든다. throughput과 P99 latency가 동시에 개선되는 것은 trade-off 해소가 아니라 비효율 제거다.
- Long prompt의 TPOT spike는 chunked prefill로 chunk size와 TTFT의 trade-off를 SLO에 맞춰 조정함으로써 해결한다.
- Prefill과 decode의 hardware optimal이 다르다는 사실은 disaggregation을 고트래픽 환경에서 경제적으로 정당화한다.