본문 바로가기

08. continuous batching

핵심 질문
  • "매 반복마다 배치를 재구성한다"는 것은 구체적으로 무엇을 결정하는 일인가?
  • continuous batching은 어떤 별개의 결정들(admission·preemption·allocation)로 이루어지는가?

목표
  • iteration 단위 스케줄링을 정의하고 static batch와 대비한다.
  • admission(수용)·preemption(선점)·allocation(할당)을 분리해 이해한다.

여정에서의 위치

07장에서 static batch의 낭비(padding·straggler)의 뿌리가 "배치를 통째로 시작·종료함"이라고 봤습니다. continuous batching(연속 배칭)은 그 뿌리를 바꿉니다: 배치를 고정하지 않고 매 반복(iteration = 토큰 한 스텝 생성)마다 다시 구성합니다.

flowchart LR
    IT[iteration 시작]:::ctrl --> RUN[현재 배치의 요청들<br/>각 1 토큰 생성]:::comp
    RUN --> FIN{끝난 요청?}:::ctrl
    FIN -->|즉시 내보냄| OUT[완료 요청 방출]:::result
    FIN --> FREE[KV 블록 회수]:::mem
    FREE --> ADMIT{빈자리에 새 요청?}:::ctrl
    ADMIT -->|admission| NEW[대기열에서 승격]:::result
    NEW --> IT
    OUT --> IT

    classDef ctrl fill:#c3fae8,stroke:#0ca678,color:#000;
    classDef comp fill:#d0ebff,stroke:#1971c2,color:#000;
    classDef mem fill:#e5dbff,stroke:#7048e8,color:#000;
    classDef result fill:#d3f9d8,stroke:#2f9e44,color:#000;

끝난 요청은 기다리지 않고 즉시 배치를 떠나고(straggler 완화), 그 빈자리에 대기 요청이 들어옵니다. 이것이 iteration-level scheduling입니다.

🔎 iteration의 뜻: 여기서 iteration은 "배치 안 각 요청이 토큰을 한 번씩 만드는 한 스텝"입니다. static batch가 "요청 단위"로 스케줄한다면, continuous batching은 "스텝 단위"로 스케줄합니다.

하나의 스케줄링을 세 결정으로 분리

continuous batching을 "그냥 잘 섞는 것"으로 뭉뚱그리면 이해가 흐려집니다. 실제로는 분리된 세 결정이 매 반복 맞물립니다.

1. admission (수용) — 누구를 배치에 넣을까

대기열의 어떤 요청을 이번 반복부터 실행 배치에 올릴지 결정합니다. 새 요청의 prefill을 언제 끼울지도 여기 포함됩니다. 무한정 받으면 KV가 고갈되므로, KV 여유가 있을 때만 수용합니다.

2. preemption (선점) — 누구를 잠시 빼낼까

KV 공간이 부족해지면, 실행 중인 요청 중 일부를 잠시 내보내 자리를 확보해야 할 수 있습니다. 선점된 요청의 KV는 회수되고(또는 다른 곳으로 옮겨지고), 나중에 다시 들어올 때 상태를 복원하거나 재계산합니다. 선점은 공짜가 아니라 재개 비용을 동반합니다.

3. allocation (할당) — KV 블록을 어떻게 줄까

수용·유지되는 각 요청에 KV 저장 공간을 배정합니다. 요청의 컨텍스트가 자라면(05장) 블록을 더 줘야 하고, 끝나면 회수합니다. 이 할당을 낭비 없이 유연하게 하는 것이 다음 장 PagedAttention의 주제입니다.

결정질문실패 시 증상
admission지금 새 요청을 받을까?너무 받으면 KV 고갈, 너무 안 받으면 활용률↓
preemption자리를 위해 뺄까?잦으면 재개 비용↑, 지연 튐
allocationKV 블록을 어떻게 줄까단편화 심하면 실제 수용량↓
📌 핵심
왜 분리해서 보나: 이 셋은 서로 다른 목표를 갖고 때로 충돌합니다. admission은 활용률을, preemption은 공정성·생존을, allocation은 메모리 효율을 겨냥합니다. 벤치마크에서 "continuous batching이 빠르다"는 주장을 볼 때, 어느 결정의 개선인지를 물으면 훨씬 정확히 읽힙니다.

chunked prefill — 여정을 다시 섞다

여기서 02장에서 예고한 chunked prefill이 등장합니다. 긴 prefill 하나가 배치를 독점하면 다른 요청의 decode가 굶습니다(starvation). 그래서 긴 prefill을 여러 조각으로 나눠 decode 스텝들 사이에 끼워 넣습니다.

⚖️ 절충: chunked prefill은 긴 프롬프트 요청의 TTFT를 약간 늘리는 대신, 다른 요청들이 굶지 않게 해 전체 goodput을 지킵니다(14장). "누군가의 지연↑ ↔ 전체 공정성↑"의 절충입니다.

이 지점에서 02장의 "phase가 시간상 깔끔히 나뉜다"는 그림이 왜 흐려지는지 분명해집니다 — prefill 조각과 decode 스텝이 시간축에서 뒤섞이기 때문입니다.

사례로 보기: vLLM V1의 스케줄러 (버전 고정)

vLLM V1(2025-01 재설계 이후, 확인일 2026-07-20) 기준의 한 사례입니다. V1 스케줄러는 prefill/decode를 스케줄링 관점에서 균일하게 다루며, 스케줄링 결정을 {request_id: 토큰 수} 형태의 딕셔너리로 표현하는 것으로 설명됩니다(01장에서 언급). 스케줄러가 요청을 RUNNING 상태로 옮기며 KV 블록을 할당하는 흐름으로 문서화돼 있습니다.

🔬 증거 읽기: 위는 vLLM 공식 블로그·문서·저장소(PRIMARY, vendor-reported)에 근거한 설계 서술입니다. "그래서 continuous batching이 다른 방식보다 몇 % 빠르다"는 별개의 measured 주장이며, 모델·워크로드·하드웨어·버전이 명시돼야 합니다. 컴포넌트 명칭·기본값은 버전마다 바뀌므로 원 설계와 특정 버전 명칭을 구분하세요.

📌 핵심

  • continuous batching은 배치를 iteration마다 재구성 → straggler·padding 낭비 완화.
  • 하나의 스케줄링이 아니라 admission·preemption·allocation 세 결정의 맞물림.
  • preemption은 재개 비용을 동반하고, chunked prefill은 개별 지연↑ ↔ 전체 공정성↑의 절충.
  • KV allocation의 효율 문제가 다음 장(PagedAttention)으로 이어진다.

🔎 이 장의 '지지하지 않는 결론'

  • "continuous batching은 항상 더 빠르다"고 말할 수 없습니다 — 선점·재개 비용, 워크로드 의존성이 있습니다.
  • vLLM 스케줄러 서술로부터 성능 우열을 판정할 수 없습니다(vendor 설계 서술 ≠ 독립 measured 비교).

✍️ 확인 문제

  1. static batch 대비 continuous batching이 straggler 낭비를 줄이는 원리를 "언제 배치를 재구성하는가"로 설명하세요.
  2. admission·preemption·allocation을 각각 한 줄로 정의하고, 셋이 충돌할 수 있는 예를 하나 드세요.
  3. (조건 유형) "우리 벤치마크에서 continuous batching이 X% 빨랐다"를 신뢰하려면 어떤 조건들이 함께 공개돼야 합니까?
다음 장: 09 · PagedAttention — KV allocation의 낭비를 겨냥한 블록 설계.