Field Log · Entry
Continuous Batching·Chunked Prefill: LLM Scheduler 설계 (4/10)
오늘의 결론
- Static batching은 가장 긴 요청이 끝날 때까지 batch slot을 묶어 둡니다. Continuous batching은 매 decode iteration에 완료 요청을 빼고 새 요청을 넣어 GPU 유휴 slot을 줄입니다.
- 긴 prompt 전체를 한 iteration에 넣으면 이미 생성 중인 요청의 다음 token이 지연됩니다. Chunked prefill은 prompt를 여러 token chunk로 나눠 decode와 같은 iteration budget 안에 배치합니다.
- “Decode를 항상 먼저”만으로는 충분하지 않습니다. Decode ITL은 안정되지만 새 prefill이 굶어 TTFT가 폭증할 수 있습니다. Waiting age, deadline, tenant service, remaining work를 함께 봅니다.
max batched tokens는 안전한 시작점이지 보편적 SLO가 아닙니다. 같은 token 수라도 context length, prefill/decode 혼합, model·GPU·kernel에 따라 iteration 시간이 달라집니다.- Scheduler의 최종 목적은 peak token/s가 아니라 TTFT와 ITL을 동시에 만족한 goodput입니다. Admission·KV capacity·preemption·cancellation까지 하나의 control loop로 검증해야 합니다.
앞 글에서는 prefill의 큰 GEMM과 decode의 작은 GEMV형 kernel을 구분했습니다. 이번 글은 서로 다른 shape의 요청을 어떤 순서와 크기로 같은 GPU iteration에 넣을지 결정합니다.
arrivals + waiting queue + active sequences + free KV blocks
→ admission
→ priority and fairness
→ iteration token/time budget packing
→ prefill chunk + one-token decodes
→ model execution
→ KV update + completion + preemption
→ next iteration
이 글에서 답하는 질문
- Static·dynamic·continuous batching은 무엇이 다른가?
- Iteration-level scheduling이 autoregressive generation에 맞는 이유는 무엇인가?
- 긴 prefill이 decode의 ITL을 망가뜨리는 과정은 무엇인가?
- Chunk size와 token budget은 어떤 지표로 조정하는가?
- KV memory가 부족할 때 swap·recompute·reject 중 무엇을 선택하는가?
- Priority와 tenant fairness를 어떻게 함께 보장하는가?
그림 1. Continuous batching의 핵심 상태는 request 수가 아니라 iteration마다 실행할 token과 이미 점유한 KV다. Chunked prefill은 긴 작업을 쪼개지만 정책이 없으면 starvation과 fragmentation이 생긴다.
1. 왜 일반적인 Batch가 맞지 않는가
분류 model은 batch 전체가 한 번의 forward로 끝납니다. LLM generation은 요청마다 prompt와 output 길이가 다르고, 종료 시점도 다릅니다.
request A: prompt 200 → output 20
request B: prompt 4000 → output 300
request C: prompt 800 → output 60
세 요청을 static batch로 묶으면 A가 끝난 뒤에도 B가 끝날 때까지 A의 slot이 비거나 padding 계산이 생깁니다. 새 요청 D도 기존 batch가 완료될 때까지 기다립니다.
2. Static, Dynamic, Continuous Batching
Static Batching
미리 정한 요청을 batch로 묶고 모두 끝날 때까지 구성원을 바꾸지 않습니다. Offline throughput에는 단순하지만 online latency와 길이 편차에 약합니다.
Dynamic Batching
짧은 대기 window 동안 도착한 요청을 묶어 batch를 시작합니다. Batch 시작 전 packing은 좋아지지만 generation 중 구성원은 대체로 고정됩니다.
Continuous Batching
매 model iteration이 끝날 때 구성원을 다시 고릅니다.
iteration t
→ finished sequence 제거
→ cancelled sequence 정리
→ active decode 유지 또는 preempt
→ waiting prefill admit
→ iteration t+1 batch 구성
이 때문에 iteration-level batching이라고도 부릅니다.
3. Request가 아니라 Iteration을 Scheduling한다
Orca는 request 전체가 끝날 때까지 engine을 점유하는 대신, scheduler가 model의 한 iteration만 실행하도록 요청하는 iteration-level scheduling을 제시했습니다.
Autoregressive decode는 token 하나를 만든 뒤 다음 token의 입력이 결정됩니다. 그 경계에서 다음 batch를 구성하면 이미 완료된 요청을 즉시 빼고 새 요청을 넣을 수 있습니다.
t0: [A decode, B decode, C prefill]
t1: [A decode, B decode, D prefill] # C finished prefill, starts decode later
t2: [B decode, C decode, D decode] # A finished
Scheduler overhead가 매 step 발생하지만 GPU slot과 KV memory를 더 유연하게 사용합니다.
4. Scheduler가 관리할 State
Request마다 최소한 다음 상태가 필요합니다.
| 상태 | 의미 |
|---|---|
WAITING | Admission 또는 첫 prefill을 기다림 |
PREFILLING | Prompt 일부 또는 전체를 처리 중 |
DECODING | 매 iteration 한 개 이상의 token 생성 |
PREEMPTED | KV를 내렸거나 버리고 재개 대기 |
TOOL_WAIT | 외부 작업을 기다리며 KV 정책 적용 |
FINISHED | EOS·length·cancel·error로 종료 |
필드도 request count보다 구체적이어야 합니다.
arrival_time · tenant · priority · deadline
prompt_tokens · cached_prefix_tokens · prefilled_tokens
generated_tokens · reserved_output_tokens
physical_kv_blocks · adapter · grammar
last_scheduled_at · accumulated_service
5. Prefill과 Decode는 같은 비용이 아니다
Prefill은 prompt token을 병렬 처리해 큰 GEMM을 만들고 compute utilization이 높습니다. Decode는 active sequence마다 보통 token 하나를 처리하며 weight와 긴 KV를 읽어 memory-bound가 되기 쉽습니다.
같은 iteration에 둘을 섞으면 decode batch의 남는 compute를 prefill이 활용할 수도 있지만, 긴 prefill 하나가 iteration 시간을 크게 늘릴 수도 있습니다.
decode-only iteration: 12 ms
decode + full 8K prefill: 180 ms
decode + 512-token prefill: 18 ms
이는 설명용 예시값입니다. 실제 차이는 model·GPU·batch·context·kernel에서 측정합니다.
6. Full Prefill이 Head-of-Line Blocking을 만든다
요청 A가 매 15ms마다 token을 받고 있는데, 새 요청 B의 16K prompt 전체가 같은 iteration에 들어왔다고 가정합니다.
time ─────────────────────────────────────────────▶
A decode: ● ● ● [──────── waits for B prefill ────────] ●
B input : [=========== full prefill =============]
B의 TTFT는 빨라질 수 있지만 A의 inter-token latency가 깨집니다. 긴 prompt 하나가 뒤의 요청과 기존 decode를 막는 전형적인 head-of-line 문제입니다.
7. Chunked Prefill의 기본 원리
긴 prompt를 token 축으로 나눕니다.
16K prompt
→ chunk 0: tokens 0..511
→ chunk 1: tokens 512..1023
→ ...
→ chunk 31: tokens 15872..16383
각 chunk는 앞 chunk까지 만든 KV를 사용해 다음 구간을 처리합니다. 모든 chunk가 끝나야 첫 output token을 생성할 수 있지만, chunk 사이에서 active decode를 scheduling할 수 있습니다.
8. Chunked Timeline
time ─────────────────────────────────────────────▶
A decode: ● ● ● ● ● ● ● ●
B input : [C0] [C1] [C2] [C3] ... [C31] → first token
Chunking은 B의 prefill을 공짜로 만들지 않습니다. 총 FLOP는 비슷하고 boundary overhead가 늘 수 있습니다. 대신 한 iteration의 최대 시간을 제한해 기존 decode의 stall을 줄입니다.
9. Iteration Token Budget
가장 이해하기 쉬운 scheduler는 한 iteration에 처리할 token 상한 B를 둡니다.
B = 2048 scheduled tokens
active decodes = 96 × 1 token = 96
remaining budget = 1952
prefill chunk ≤ 1952 tokens
Sarathi-Serve의 stall-free scheduling은 먼저 running decode를 batch에 넣고, 남은 budget에 partial prefill과 새 prefill을 배치하는 방향을 제시합니다.
10. 기본 Packing 순서
초기 구현은 다음처럼 만들 수 있습니다.
def build_iteration(running, waiting, token_budget):
batch = []
remaining = token_budget
# 1. 진행 중 decode의 다음 token을 보호한다.
for req in eligible_decodes(running):
if remaining < 1:
break
batch.append((req, "decode", 1))
remaining -= 1
# 2. 이미 시작한 prefill을 이어 starvation을 줄인다.
for req in partial_prefills(running):
n = min(req.remaining_prompt, remaining)
if n == 0:
break
batch.append((req, "prefill", n))
remaining -= n
# 3. KV admission을 통과한 새 prefill을 시작한다.
for req in ranked_waiting(waiting):
n = min(req.uncached_prompt, remaining)
if n == 0 or not kv_can_reserve(req, n):
continue
batch.append((req, "prefill", n))
remaining -= n
return batch
실제 engine에는 graph bucket, adapter grouping, TP synchronization, prefix hit, max sequence 수 같은 제약이 추가됩니다.
11. Token Budget만으로 시간이 같아지지 않는다
Token 1개의 비용은 phase와 context에 따라 다릅니다.
- Prefill token은 큰 GEMM과 attention tile에 들어갑니다.
- Decode token은 해당 sequence의 전체 과거 KV를 읽습니다.
- Quantized model은 dequantization과 kernel path가 다릅니다.
- LoRA·grammar·speculative verification은 별도 cost를 더합니다.
- TP degree와 collective도 iteration 시간을 바꿉니다.
따라서 scheduled_tokens ≤ B는 proxy입니다. 더 정교한 정책은 offline profile이나 online telemetry로 iteration time을 예측합니다.
$$ \hat{T}{iter}=f(N{decode},;L_{context},;N_{prefill},;phase\ mix,;backend) $$
12. SLO에서 Budget을 역산한다
Decode target이 ITL p95 ≤ 50ms라면 scheduler가 사용할 수 있는 iteration budget을 profile로 찾습니다.
- Decode batch·context length grid를 측정합니다.
- 각 grid에 prefill token을 점진적으로 섞습니다.
- ITL target을 넘기기 직전 token/time budget을 찾습니다.
- Production 분포와 burst에서 p95·p99를 확인합니다.
- Model·GPU·kernel revision마다 다시 calibration합니다.
Static budget은 단순하지만 workload 변화에 둔감합니다. 2026년 공개된 latency-control 연구는 predictor와 active prefill control로 고정 token budget의 한계를 다루지만, 아직 preprint이므로 target engine에서 독립 검증해야 합니다.
13. Chunk Size의 Trade-off
너무 큰 Chunk
- Prefill kernel efficiency는 좋을 수 있습니다.
- TTFT는 줄어들 수 있습니다.
- Decode ITL spike가 커집니다.
- Priority request의 preemption point가 늦어집니다.
너무 작은 Chunk
- Decode를 자주 끼울 수 있습니다.
- Kernel launch와 scheduling overhead가 늘어납니다.
- Weight를 여러 번 읽고 pipeline bubble이 생길 수 있습니다.
- MoE에서는 expert weight reload가 늘 수 있습니다.
하나의 전역 chunk size보다 model·phase·load에 따른 bucket 또는 predicted-time chunk가 더 적합할 수 있습니다.
14. Decode-First에도 Starvation이 있다
항상 모든 decode를 먼저 넣고 load가 높으면 남는 budget이 거의 없을 수 있습니다. 그러면 waiting request의 prefill이 시작되지 못해 TTFT가 무한히 커집니다.
decode queue continuously full
→ all token budget consumed
→ new prefills never progress
→ stable ITL, broken TTFT
안전장치로 다음을 조합합니다.
- Prefill 최소 service share
- Waiting age에 따른 priority boost
- TTFT deadline slack
- Partial prefill completion boost
- Decode batch cap 또는 separate pool
- Admission 단계의 overload rejection
15. FCFS의 장점과 한계
First-Come-First-Served는 설명 가능하고 starvation이 적습니다. 그러나 맨 앞의 64K prompt가 뒤의 200-token prompt를 막을 수 있습니다.
Shortest-prompt-first는 평균 TTFT를 줄일 수 있지만 긴 prompt가 굶을 수 있습니다. Shortest-remaining-processing-time도 output 길이를 미리 정확히 모른다는 한계가 있습니다.
그래서 production policy는 단일 정렬 key보다 class와 aging을 사용합니다.
effective priority
= product class
+ deadline urgency
+ waiting age boost
− estimated remaining cost penalty
16. Output Length를 모른다는 문제
Prompt length는 admission 전에 알지만 실제 output length는 생성이 끝나야 압니다. 사용자의 max_tokens는 상한이지 예측값이 아닙니다.
FastServe는 autoregressive token boundary에서 preemption하고, input length를 활용한 skip-join multi-level feedback queue를 제안했습니다. 핵심은 service를 받은 양과 실제 진행을 보며 priority를 조정하는 것입니다.
Production에서는 endpoint·tenant·task별 output quantile을 사용하되, 예측 실패를 위해 KV headroom과 demotion·aging을 둡니다.
17. Tenant Fairness는 Request 수가 아니다
Tenant A가 100-token prompt 10개를 보내고 B가 10K-token prompt 1개를 보내면 요청 수 1:1 fairness는 실제 GPU service와 다릅니다.
VTC 연구는 input·output token의 cost를 반영하는 virtual token counter로 continuous batching 환경의 service fairness를 다룹니다.
tenant service
= weighted input tokens
+ weighted output tokens
+ optional KV residency / premium class cost
공정성은 모든 요청의 동일 latency가 아니라, backlog가 있는 tenant 사이에서 정의한 service cost가 지나치게 벌어지지 않도록 하는 계약입니다.
18. Aging과 Deadline을 함께 쓴다
Aging은 오래 기다린 작업의 priority를 점차 높여 starvation을 막습니다. Deadline urgency는 남은 slack을 봅니다.
slack = deadline − now − predicted_remaining_time
Slack이 음수가 된 요청을 무조건 최우선으로 두면 이미 실패한 요청이 전체 system을 망칠 수 있습니다. 다음 action을 명시합니다.
- SLO 달성 가능: priority boost
- 달성 불확실: best-effort queue 또는 downgrade
- 달성 불가능: 빠른 reject·fallback model·다른 replica route
19. Admission과 Scheduling을 분리한다
Scheduler가 실행 순서를 잘 정해도 GPU가 감당할 수 없는 요청을 모두 받아 놓으면 queue만 길어집니다.
Admission
“이 요청을 지금 system에 받아도 SLO와 KV capacity를 지킬 가능성이 있는가?”를 결정합니다.
Scheduling
“이미 받은 요청 중 다음 iteration에 무엇을 실행할까?”를 결정합니다.
arrival
→ admission: accept / queue upstream / reject / route
→ scheduler: run / wait / preempt / resume
Admission에는 uncached prompt, reserved output, physical KV block, tenant quota, predicted queue delay를 사용합니다.
20. KV Memory는 Scheduling Constraint다
Active decode를 하나 더 넣는 계산 token은 1개지만, 그 요청은 이미 수천 token의 KV를 점유하고 다음 token마다 더 자랍니다.
따라서 두 budget을 따로 봅니다.
iteration compute budget
→ 이번 step에 실행할 prefill + decode tokens
resident KV budget
→ 현재 physical tokens + future growth reservation
Compute budget이 남아도 KV block이 없으면 새 prefill을 시작하면 안 됩니다. 반대로 KV가 남아도 iteration time budget이 없으면 기다려야 합니다.
21. Preemption의 세 선택
Recompute
Request의 KV를 버리고 나중에 prompt와 generated prefix를 다시 prefill합니다.
- Host transfer가 없습니다.
- 긴 context에서는 FLOP와 TTFT 비용이 큽니다.
- Stable prefix cache가 있으면 일부 재사용할 수 있습니다.
Swap or Offload
KV를 host memory 또는 다른 tier로 옮기고 나중에 복원합니다.
- Recompute FLOP를 줄입니다.
- PCIe·network bandwidth와 host capacity가 필요합니다.
- Resume latency 변동이 생깁니다.
Reject or Route
아직 service를 거의 받지 않은 waiting request는 다른 replica로 보내거나 즉시 overload 응답을 주는 편이 나을 수 있습니다.
정책은 KV byte, expected pause, transfer bandwidth, recompute time을 비교합니다.
22. 누구를 Preempt할까
단순히 가장 최근 요청을 고르는 것보다 cost를 계산합니다.
victim score
= freed_KV_bytes
− recompute_or_swap_cost
− deadline_penalty
− fairness_debt
− nearly_finished_bonus
출력이 거의 끝난 요청을 내리면 곧 반환될 KV와 완료 goodput을 잃습니다. 반대로 매우 긴 context 하나가 큰 KV를 점유할 때는 preempt 효과가 큽니다.
23. Cancellation은 즉시 반영한다
Client disconnect, timeout, Agent branch 취소가 생겼는데 다음 iteration까지 계속 생성하면 compute와 KV를 낭비합니다.
검증할 경계:
- HTTP disconnect가 scheduler까지 전달되는 시간
- In-flight kernel 뒤 request 제거
- KV block과 prefix reference 반환
- Streaming queue와 sampler state 정리
- Distributed rank의 동일한 cancellation view
- Cancelled token이 billing·metric에 섞이지 않는지
Cancellation storm도 부하 시험에 포함합니다.
24. Prefix Cache-Aware Scheduling
두 worker 중 하나가 요청의 긴 prefix KV를 가지고 있다면 단순히 queue가 짧은 worker로 보내는 것이 최적이 아닐 수 있습니다.
route cost
= predicted queue delay
+ uncached prefill time
+ KV transfer or recompute time
Preble은 distributed prompt scheduling에서 KV reuse와 load balancing을 함께 최적화하는 문제를 다룹니다. Cache locality만 우선하면 hot worker가 과부하되고, load만 보면 긴 prefix를 반복 계산합니다.
25. Adapter와 Grammar도 Batch를 갈라놓는다
요청이 같은 base model을 사용해도 다음 차이가 kernel·graph compatibility를 바꿀 수 있습니다.
- LoRA adapter와 rank
- Sampling strategy
- Structured generation grammar
- Attention mask·sliding window
- Speculative decoding state
- Vision input 또는 encoder state
Scheduler가 모든 요청을 한 batch에 억지로 섞으면 fallback 또는 extra kernel이 생길 수 있습니다. 반대로 adapter별로 완전히 분리하면 작은 batch가 됩니다. Compatibility와 queue delay의 trade-off를 측정합니다.
26. Prefill/Decode Disaggregation은 언제 필요한가
Chunked prefill은 같은 GPU에서 interference를 제한합니다. 그러나 TTFT와 ITL SLO가 모두 매우 빡빡하거나 phase별 최적 parallelism이 크게 다르면 prefill worker와 decode worker를 분리할 수 있습니다.
prefill pool
→ compute prompt and produce KV
→ transfer KV over high-bandwidth path
decode pool
→ consume KV and generate tokens
DistServe는 두 phase를 다른 GPU에 배치해 interference와 resource coupling을 줄이는 방향을 제시합니다. 대신 KV transfer, placement, failure boundary, pool imbalance가 새 병목이 됩니다. 한 GPU scheduler가 어렵다는 이유만으로 바로 분리하지는 않습니다.
27. RAG Agent Workload의 Scheduler 특징
Retrieved Context Variance
Query마다 검색된 chunk 수와 길이가 달라 prefill 분산이 큽니다. Context cap뿐 아니라 uncached token과 expected quality gain을 함께 봅니다.
Shared System and Tool Prefix
System prompt와 tool schema가 같으면 prefix hit를 routing과 admission에 반영합니다. Schema revision을 cache identity에 포함합니다.
Tool Wait
짧은 tool call은 KV를 유지하고, 긴 외부 job은 offload 또는 recompute할 수 있습니다. TOOL_WAIT를 active decode로 계속 세면 concurrency를 과대평가합니다.
Multi-step Deadline
한 model call의 TTFT가 좋아도 retrieve→generate→tool→generate 전체 deadline을 놓칠 수 있습니다. 남은 Agent step budget을 scheduler priority에 전달합니다.
Parallel Branch
여러 plan을 동시에 decode하면 tenant service와 KV를 급격히 사용합니다. Branch별 budget과 cancel propagation이 필요합니다.
28. Scheduler Event Log
결과만 남기면 tail latency 원인을 알 수 없습니다. Request별 state transition을 sampled trace로 기록합니다.
{
"request_id": "r-1842",
"event": "scheduled",
"phase": "prefill",
"scheduled_tokens": 512,
"prefilled_tokens": 1536,
"remaining_prompt_tokens": 2048,
"active_decodes": 72,
"token_budget": 2048,
"predicted_iteration_ms": 31.4,
"free_kv_blocks": 9210,
"priority_reason": "aging_boost"
}
개인정보가 있는 prompt 본문 대신 length, cache identity hash, class, reason code를 남깁니다.
29. 핵심 Metric
Queue와 Admission
- Arrival·admitted·rejected request/s
- Waiting requests와 waiting tokens
- Queue delay p50·p95·p99
- Admission reason code
- Tenant별 service debt
Iteration
- Iteration duration histogram
- Prefill·decode scheduled tokens
- Active sequence와 batch token
- Chunk size와 partial prefill 수
- CUDA Graph bucket·fallback
- GPU idle gap
Request SLO
- TTFT
- Inter-token latency 또는 TPOT
- End-to-end latency
- SLO attainment와 goodput
- Preempt·swap·recompute·cancel rate
평균 token/s가 좋아도 일부 tenant의 TTFT p99가 무너지면 scheduler는 성공하지 못했습니다.
30. Open-loop 부하로 검증한다
Closed-loop client는 응답이 느려질수록 새 요청도 늦게 보내 overload를 숨깁니다. Production 도착률을 검증하려면 open-loop arrival을 사용합니다.
- 실제 prompt·output joint distribution을 보존합니다.
- Short·long prompt와 reasoning output을 섞습니다.
- Prefix hit·miss, adapter, grammar cohort를 분리합니다.
- Arrival burst와 tenant skew를 재현합니다.
- Tool wait·cancel·timeout을 주입합니다.
- Rate를 높이며 queue가 안정적으로 수렴하는 지점을 찾습니다.
- TTFT와 ITL SLO를 동시에 만족한 최대 rate를 goodput으로 기록합니다.
- 30분 이상 실행해 KV fragmentation과 starvation을 확인합니다.
31. Budget Tuning 실험
다음 grid를 한 번에 모두 바꾸지 말고 순차적으로 비교합니다.
max batched tokens: 512, 1024, 2048, 4096
max active seqs: 32, 64, 128, 256
prefill chunk: 256, 512, 1024, adaptive
policy: FCFS, decode-first, aging, deadline-aware
preemption: recompute, swap, route
각 조합에서 TTFT·ITL Pareto frontier, goodput/GPU, KV headroom, fairness를 기록합니다. 하나의 “최고 throughput” 설정 대신 endpoint SLO별 profile을 선택합니다.
32. 최소 구현 순서
- Request state machine과 finish·cancel transition을 정의합니다.
- 매 iteration active decode token을 정확히 셉니다.
- Fixed token budget으로 continuous batching을 구현합니다.
- Long prefill을 chunk하고 partial progress를 KV와 함께 보존합니다.
- Compute token과 resident KV budget을 분리합니다.
- Admission에 future output growth와 queue delay를 넣습니다.
- Decode-first에 prefill minimum share와 aging을 추가합니다.
- Preemption victim과 recompute·swap policy를 정의합니다.
- Event log와 TTFT·ITL·goodput dashboard를 만듭니다.
- Production-shaped open-loop load로 budget을 calibration합니다.
- Prefix locality와 tenant fairness를 추가합니다.
- Colocation 한계가 측정된 뒤 disaggregation을 검토합니다.
실전 체크리스트
- Static·dynamic·continuous batching을 구분했다.
- Request가 아니라 iteration 경계에서 batch를 재구성한다.
- Active decode와 partial prefill 상태를 따로 관리한다.
- 긴 prompt 전체가 decode를 stall하지 않도록 chunk한다.
- Chunk size의 kernel 효율과 ITL trade-off를 측정했다.
- Request 수가 아니라 scheduled token을 기록한다.
- Token budget과 predicted iteration time의 오차를 본다.
- Decode-first로 인한 prefill starvation 안전장치가 있다.
- FCFS 외에 aging·deadline·remaining work를 검토했다.
- Tenant fairness를 input·output service cost로 정의했다.
- Compute budget과 resident KV budget을 분리했다.
- Output growth를 admission 시 reserve한다.
- Recompute·swap·route의 cost model이 있다.
- Nearly-finished request를 불필요하게 preempt하지 않는다.
- Cancellation 뒤 KV가 즉시 반환되는지 검증했다.
- Prefix cache locality와 worker load를 함께 routing한다.
- Adapter·grammar가 batch compatibility에 미치는 영향을 측정했다.
- Event log에 schedule·wait·preempt reason이 있다.
- Open-loop burst와 tenant skew를 시험했다.
- TTFT와 ITL을 동시에 만족한 goodput으로 release한다.
스스로 확인하기
Q1. Continuous batching이 static batching보다 GPU slot을 잘 쓰는 이유는 무엇인가?
매 generation iteration 뒤 완료·취소 요청을 빼고 새 요청을 넣기 때문입니다. 출력 길이가 서로 달라도 가장 긴 요청이 끝날 때까지 batch 구성을 고정하지 않습니다.
Q2. Chunked prefill은 전체 prefill FLOP를 줄이는가?
대체로 아닙니다. 긴 prompt를 여러 iteration으로 나눠 decode가 기다리는 최대 시간을 줄입니다. 작은 chunk의 launch·weight reload overhead가 추가될 수도 있습니다.
Q3. Decode-first 정책의 대표 실패는 무엇인가?
높은 부하에서 decode가 token budget을 계속 소비해 waiting prefill이 진행되지 않는 starvation입니다. Minimum prefill share, aging, deadline, admission control이 필요합니다.
Q4. max_num_batched_tokens=2048이면 iteration 시간이 항상 같은가?
아닙니다. Prefill/decode 비율, decode context length, dtype, kernel, graph bucket, TP collective에 따라 같은 token 수의 시간이 달라집니다. Profile과 online telemetry로 calibration해야 합니다.
Q5. Chunked prefill과 prefill/decode disaggregation의 차이는 무엇인가?
Chunking은 같은 worker에서 prefill을 잘게 나눠 decode와 섞습니다. Disaggregation은 두 phase를 다른 GPU pool로 분리해 interference를 제거하지만 KV transfer와 placement 비용을 추가합니다.
다음 글
이번 글은 token을 언제 실행할지 결정했습니다. 다음 글 Paged KV Cache와 Prefix Caching: Block·Eviction·Reuse 설계에서는 scheduler가 의존하는 physical KV block을 어떻게 할당하고 공유하며 회수할지 살펴봅니다.
참고문헌
- Yu et al., Orca: A Distributed Serving System for Transformer-Based Generative Models, OSDI 2022.
- Kwon et al., Efficient Memory Management for Large Language Model Serving with PagedAttention, SOSP 2023.
- Agrawal et al., Taming Throughput-Latency Tradeoff in LLM Inference with Sarathi-Serve, OSDI 2024.
- Wu et al., Fast Distributed Inference Serving for Large Language Models, 2023.
- Sheng et al., Fairness in Serving Large Language Models, OSDI 2024.
- Zhong et al., DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving, OSDI 2024.
- Srivatsa et al., Preble: Efficient Distributed Prompt Scheduling for LLM Serving, 2024.
- Liu et al., Fairness-Aware and Latency-Controllable Scheduling for Chunked-Prefill LLM Serving, preprint, 2026.
검증 메모 — 개념과 문헌은 2026년 7월 16일 확인했습니다. Scheduler 설정과 backend 기능은 engine revision마다 달라집니다. 본문의 token·latency 수치는 원리를 설명하는 예시값이며 실제 prompt·output·arrival·prefix 분포로 open-loop 부하를 만들고 TTFT·ITL·goodput을 다시 측정해야 합니다. 2026년 fairness·latency-control 문헌은 동료평가 전 preprint로 구분했습니다.