AEGLOSLAB 엔비디아 기술 스택

제5부 추론 최적화와 서빙

5.13 Triton III — 동적·시퀀스 배칭

5.13.1 이 장에서 배우는 것 / 선수 지식

5.12에서 인스턴스로 동시성을 올렸다. 이제 직교하는 다른 레버 — 배칭 — 을 서버 수준에서 판다. 추론 요청은 사용자가 하나씩 보내는데(5.1), 그것을 하나씩 처리하면 메모리 바운드 구간에서 GPU를 못 채운다(5.1.4). 동적 배칭(dynamic batching) 은 들어오는 요청을 잠깐 모아 한 배치로 묶어 처리량을 끌어올린다 — 대가는 큐 대기 지연이다. 이 "지연↔처리량" 저울이 이 장의 핵심이다.

여기에 더해, 상태 있는 모델(시퀀스·스트리밍처럼 요청 간 상태를 유지)을 위한 시퀀스 배칭(sequence batching), 그리고 LLM의 인플라이트 배칭(5.8) 과의 구분을 명확히 한다 — 세 배칭은 다른 문제를 푼다.

배우는 것:

  • 동적 배칭 — 서버가 요청을 모아 배치를 만드는 메커니즘, 핵심 손잡이 preferred_batch_size·max_queue_delay 와 지연↔처리량 트레이드오프.
  • 동적 배칭과 인스턴스(5.12)의 관계 — 배치가 각 실행을 크게, 인스턴스가 여러 실행 동시.
  • 시퀀스 배칭 — 상태 있는 모델을 위한 correlation ID·start/end·라우팅(direct vs oldest 전략).
  • 세 배칭의 구분 — 동적(무상태 단발)·시퀀스(상태)·인플라이트(자기회귀 LLM, 5.8).
  • 우선순위·큐 정책 — priority level·queue timeout·max queue size.
  • 동적 배칭을 켜고 max_queue_delay를 튜닝하며 지연↔처리량 곡선을 측정하기(실습).

선수 지식: 5.11~5.12(config.pbtxt·instance_group·perf_analyzer), 5.1(배치=마스터 노브·지연/처리량·goodput·p99), 5.8(인플라이트 배칭 — 구분), 2.10(비동기). 환경은 5.11.

정직: dynamic_batching·sequence_batching 필드는 비교적 안정적이나 일부 옵션·기본값은 버전 의존이다 — 정확한 스키마는 그 버전 문서로 확인. 모든 처리량·지연 수치는 환경 의존 개략값, 실측.

5.13.2 동적 배칭 — 요청을 모아 묶는다

클라이언트는 요청을 개별적으로·비동기로 보낸다. 서버가 이를 하나씩 처리하면:

즉시 즉시 t0: 요청A 도착 batch=1 실행 (GPU 절반 유휴, 5.1.4) t1: 요청B 도착 batch=1 실행 매 실행이 작은 배치 → 메모리 바운드 비효율, 처리량 낮음

위는 배칭 없음 — 요청이 도착하는 즉시 batch=1로 실행하는 경우다.

동적 배칭: 도착한 요청을 큐에 잠깐 모아 더 큰 배치로 묶어 한 번에 실행한다.

preferred_batch_size 도달 또는 max_queue_delay 경과 t0: 요청A 도착 큐에 보관 (잠깐 기다림) t1: 요청B 도착 t2: 요청C·D 도착 batch=4로 한 번에 실행 큰 배치로 GPU 효율↑(처리량↑) 대신 A는 t0~t2 만큼 '큐에서 기다림'(지연↑)

핵심 트레이드오프(5.1의 배치 노브가 서버 정책으로):

기다림(max_queue_delay) ↑ 더 큰 배치 형성 가능 처리량 ↑, 지연 ↑ 기다림 ↓ 작은 배치(또는 batch=1) 지연 ↓, 처리량 ↓ SLA(지연 제약) 안에서 처리량 최대가 되는 '기다림'을 찾는 것이 튜닝

5.13.3 동적 배칭 설정·손잡이

config.pbtxt에 dynamic_batching 블록을 추가한다(max_batch_size>0 필요, 5.11):

max_batch_size: 32

dynamic_batching {
  preferred_batch_size: [ 8, 16 ]        # 이 배치 크기를 '선호'(도달하면 즉시 디스패치)
  max_queue_delay_microseconds: 1000      # 최대 큐 대기(여기선 1ms). 이 시간 지나면 현재까지로 디스패치
  # default_queue_policy { ... }          # 큐 타임아웃·최대 크기·거부 정책(아래)
  # priority_levels / priority_queue_policy ... (5.13.6)
}
손잡이의미튜닝
preferred_batch_size서버가 선호하는 배치 크기들도달 시 즉시 디스패치. 모델 효율 좋은 크기로(GEMM·텐서코어)
max_queue_delay_microseconds배치 채우려 기다리는 최대 시간핵심 손잡이. 크게=처리량↑·지연↑, 작게=반대
default_queue_policy큐 타임아웃·max 큐 크기·초과 시 거부/지연과부하 보호

작동 규칙(개략):

즉시 기다림 없이 현재까지 모인 것으로 요청이 큐에 쌓이는 동안 누적 배치가 preferred_batch_size에 도달 그 전에 max_queue_delay 경과 디스패치 부하 높으면 금방 preferred에 도달 (지연 거의 0, 처리량 최대) 부하 낮으면 max_queue_ delay까지 기다렸다 작은 배치(드문드문 트래픽 보호)

핵심: 부하가 높을 때 동적 배칭이 빛난다 — 요청이 빨리 쌓여 큰 배치가 지연 거의 없이 형성된다. 부하가 낮으면 max_queue_delay가 "너무 오래 기다리지 않게" 막아준다. max_queue_delay = SLA 여유 안에서 정한다(예: p99 예산의 일부).

5.13.4 동적 배칭 + 인스턴스 — 함께

5.12의 인스턴스와 동적 배칭은 함께 작동한다(직교 레버, 5.12.4):

동적 배칭 인스턴스 (동시 실행) 큐 → batch 16 큐 → batch 16 인스턴스1: batch16 실행 인스턴스2: batch16 실행

동적 배칭이 batch=16을 만들면 인스턴스 중 하나가 그 batch=16을 실행하고, 인스턴스가 4개면 4개의 (배치된) 실행이 동시에 진행된다 — 유효 동시성 ≈ 인스턴스 수 × 배치 크기.

튜닝은 5.12처럼 둘을 함께 스윕(Model Analyzer)한다. 일반적 경험칙:

  • 인스턴스가 복사·간극을 오버랩으로 메우고, 배칭이 각 실행의 산술 강도를 높인다(5.1.4).
  • 메모리 예산 안에서 인스턴스 × 배치를 키우다, SLA(지연)·포화·메모리의 교집합에서 멈춘다.

5.13.5 시퀀스 배칭 — 상태 있는 모델

동적 배칭은 무상태(stateless) 단발 모델용이다 — 각 요청이 독립적이라 임의로 묶어도 된다. 그러나 일부 모델은 상태(state) 를 유지한다:

상태 있는 모델의 예 스트리밍 ASR/온라인 추론 — 같은 오디오 스트림의 연속 청크가 상태 공유 상태 기반 RNN류, 또는 '대화 세션' 상태를 모델이 직접 유지하는 경우 요구: 같은 시퀀스(세션)의 요청들이 '같은 인스턴스'로 가서 '순서대로' 처리돼야 함

시퀀스 배칭은 이를 위해:

  • correlation ID: 클라이언트가 같은 시퀀스의 요청에 같은 ID를 붙임 → Triton이 같은 인스턴스·같은 슬롯으로 라우팅(상태 유지).
  • start/end 플래그: 시퀀스의 시작·끝을 표시해 모델이 상태를 초기화·정리.
  • 스케줄링 전략:
    • Direct: 각 시퀀스를 인스턴스의 특정 배치 슬롯에 고정 배정.
    • Oldest: 가장 오래 기다린 시퀀스 요청을 우선 묶음(동적 배칭과 유사하되 시퀀스 보존).
sequence_batching {
  max_sequence_idle_microseconds: 5000000     # 유휴 시퀀스 타임아웃(상태 정리)
  control_input [                              # start/end/ready/corrid 제어 텐서
    { name: "START",   control [ { kind: CONTROL_SEQUENCE_START,  ... } ] },
    { name: "END",     control [ { kind: CONTROL_SEQUENCE_END,    ... } ] },
    { name: "READY",   control [ { kind: CONTROL_SEQUENCE_READY,  ... } ] }
  ]
  # oldest { max_candidate_sequences: ... }    # 또는 direct
}

핵심: 모델이 상태를 유지하면 동적 배칭이 아니라 시퀀스 배칭이다 — 같은 세션을 같은 인스턴스로 보내 순서·상태를 보존한다. 무상태 모델에 시퀀스 배칭을 쓰거나, 상태 모델에 동적 배칭을 쓰면 상태가 깨지거나 비효율이 된다.

5.13.6 세 배칭의 구분 + 우선순위 큐

추론에서 "배칭"이 셋이라 헷갈린다. 명확히:

추론에서의 배칭 동적 배칭 (dynamic) 무상태 단발 모델. 요청 모아 한 batch로 실행. 예: 이미지 분류· 임베딩. (5.13.2) 시퀀스 배칭 (sequence) 상태 있는 모델. 같은 시퀀스를 같은 인스턴스로. 예: 스트리밍· 세션 상태 모델. (5.13.5) 인플라이트 배칭 (in-flight) 자기회귀 LLM. 스텝 단위로 요청 교체. 예: TRT-LLM/ vLLM 백엔드가 내부에서. Triton config의 동적 배칭과 다른 계층. (5.8)
배칭대상단위어디서
동적무상태 단발요청 모음Triton 스케줄러(config)
시퀀스상태 유지시퀀스(세션)Triton 스케줄러(config)
인플라이트자기회귀 LLM디코딩 스텝TRT-LLM/vLLM 백엔드(5.8)

LLM에 동적 배칭을 켜지 말 것(또는 백엔드가 알아서). LLM은 토큰을 수백 번 생성하며 요청이 제각각 끝나므로, 요청 단위로 한 번 묶는 동적 배칭이 아니라 스텝 단위 인플라이트 배칭(tensorrtllm 백엔드)이 답이다(5.8). Triton은 그 백엔드를 구동하고 큐·스트리밍·메트릭을 담당.

우선순위·큐 정책. 여러 요청 클래스(대화형 vs 배치)를 다룰 때:

dynamic_batching {
  priority_levels: 2                          # 우선순위 등급 수
  default_priority_level: 2
  priority_queue_policy {
    key: 1                                    # 높은 우선순위(예: 대화형)
    value { max_queue_size: 100, timeout_action: REJECT,
            default_timeout_microseconds: 50000 }
  }
}
  • priority level: 높은 우선순위 요청을 먼저 배치(대화형 우선, 배치 작업 후순위).
  • queue timeout / max size: 너무 오래 기다린 요청을 거부(REJECT)하거나 지연 — 과부하 시 SLA 보호(5.1 goodput).

5.13.7 워크스루 — max_queue_delay 튜닝

동적 배칭을 켜고 max_queue_delay를 바꿔가며 지연↔처리량 곡선을 측정한다.

# config.pbtxt에 dynamic_batching 추가 후 서버 재시작, perf_analyzer로 측정
# max_queue_delay_microseconds를 0, 500, 2000, 10000으로 바꿔가며(별도 config) 비교
perf_analyzer -m densenet_trt --concurrency-range 1:32:4 -u localhost:8000

# 개략 관찰(실측값으로 대체):
#   delay=0      : 처리량 ▃, p99 낮음    → 배치 잘 안 묶임(낮은 부하), 지연 우선
#   delay=500us  : 처리량 ▅, p99 약간↑   → 좋은 균형(보통 시작점)
#   delay=2ms    : 처리량 ▆, p99↑        → 처리량↑이나 지연 더 큼
#   delay=10ms   : 처리량 ▆(평탄), p99↑↑ → 처리량 이득 없이 지연만 → 과도

읽는 법:

1) 부하를 올리며(concurrency↑) 처리량·p99를 본다 2) max_queue_delay를 키우면: 처리량 곡선이 위로(포화 전까지), p99도 위로 3) '처리량이 더 안 오르는데 지연만 느는' 지점 = 과도한 delay → 그 직전이 최적 4) SLA(p99<X)를 만족하면서 처리량 최대인 (delay, preferred_batch, instance) 조합 선택 Model Analyzer로 이 3차원 (delay×batch×instance)을 자동 탐색(5.12.7)

견해: max_queue_delay는 보통 작게 시작(수백 µs~수 ms)해 SLA 여유 안에서 키운다. 부하가 높으면 delay가 거의 0이어도 배치가 금방 차므로(요청이 빨리 쌓임) 큰 delay가 불필요할 때가 많다. delay는 "낮은 부하에서도 약간은 묶이게" 하는 안전장치에 가깝다 — 과하게 키우면 지연만 손해.

5.13.8 중간 점검 (체크포인트)

  • [ ] 동적 배칭이 요청을 모아 처리량을 높이는 메커니즘과, preferred_batch_size·max_queue_delay 의 역할을 설명한다.
  • [ ] 동적 배칭의 지연↔처리량 트레이드오프와, 부하가 높을 때 delay가 거의 안 들어 효율적인 이유를 안다.
  • [ ] 동적 배칭과 인스턴스(5.12)가 함께 작동함을(유효 동시성 ≈ 인스턴스×배치) 설명한다.
  • [ ] 시퀀스 배칭(correlation ID·start/end·라우팅)이 상태 있는 모델에 왜 필요한지 안다.
  • [ ] 세 배칭(동적·시퀀스·인플라이트) 을 대상·단위·위치로 구분하고, LLM엔 인플라이트가 답임을 안다.
  • [ ] max_queue_delay를 튜닝하며 SLA 하 처리량 최대 운영점을 perf_analyzer로 찾는다.

증상→의심: 동적 배칭 켰는데 처리량 그대로 → 부하가 낮아 배치가 안 묶임(정상) 또는 max_queue_delay=0 → 부하·delay 조정. p99가 SLA 초과 → max_queue_delay 과대 또는 배치 과대 → 줄이기. 상태 모델 출력이 뒤섞임 → 동적 배칭으로 상태 깨짐 → 시퀀스 배칭으로. LLM 처리량 낮음 → 동적 배칭 오용 → tensorrtllm 인플라이트 배칭(5.8). 대화형 요청이 배치 작업에 밀림 → 우선순위 큐 미설정 → priority_levels.

5.13.9 트레이드오프·대안

배칭 전략 선택.

전략누구에게비고
동적 배칭무상태 단발(분류·검출·임베딩)처리량↑, max_queue_delay로 지연 제어
시퀀스 배칭상태 유지(스트리밍·세션 모델)correlation ID 라우팅, 상태 보존
인플라이트 배칭자기회귀 LLM백엔드(TRT-LLM/vLLM)가, Triton은 큐·스트리밍
배칭 없음(batch=1)초저지연 단건·상태 복잡처리량 낮음, 지연 최소
우선순위 큐혼합 트래픽(대화형+배치)클래스별 SLA 보호

견해: 무상태 모델은 동적 배칭 + 인스턴스(5.12)를 함께 켜고 Model Analyzer로 튜닝하는 것이 기본이다. 상태 모델은 반드시 시퀀스 배칭. LLM은 인플라이트 배칭(동적 배칭 아님). 혼합 트래픽은 우선순위 큐로 대화형을 보호. max_queue_delay는 SLA 예산 안에서 — 처리량을 위해 지연을 무한정 내주지 않는다(goodput, 5.1).

비엔비디아 정직: 동적 배칭(서버측 배칭)은 모든 서빙 시스템의 공통 기법이다(TF Serving·KServe 등도). Triton의 강점은 동적·시퀀스·(백엔드)인플라이트 배칭 + 우선순위·인스턴스의 통합과 perf_analyzer/Model Analyzer 튜닝이다. 최적 파라미터는 모델·트래픽마다 다르니 실측.

5.13.10 흔한 함정

  • max_queue_delay 과대. 처리량 이득은 포화하는데 지연만 계속 늘어 SLA·goodput을 깬다. SLA 예산 안에서, 보통 작게 시작.
  • 저부하에서 배칭 기대. 동적 배칭은 요청이 쌓여야 묶인다. 부하가 낮으면 배치가 안 커짐(정상) — delay로 약간 보완하되 처리량은 부하에 비례.
  • 상태 모델에 동적 배칭. 세션 상태가 임의의 인스턴스로 흩어져 깨진다. 시퀀스 배칭(correlation ID).
  • 무상태 모델에 시퀀스 배칭. 불필요한 라우팅 제약으로 비효율. 무상태는 동적 배칭.
  • LLM에 동적 배칭. 자기회귀를 요청 단위로 묶어선 비효율 — 인플라이트 배칭(tensorrtllm 백엔드, 5.8).
  • 인스턴스와 배칭을 따로 튜닝. 둘은 곱으로 작동 — 함께 스윕(Model Analyzer). 한쪽만 키우면 다른 병목.
  • 우선순위·큐 정책 미설정. 혼합 트래픽에서 대화형이 배치 작업에 밀려 p99 깨짐. priority_levels·queue policy로 보호.
  • 합성 균일 부하로만 튜닝. 실제는 버스트·분포가 있다. 실 트래픽 패턴으로 delay·batch를 검증.

5.13.11 연습문제

연습 1 (지연↔처리량). 동적 배칭에서 max_queue_delay를 0→2ms로 키웠다. (a) 고부하와 저부하에서 처리량·지연이 각각 어떻게 변하나? (b) 왜 고부하에서는 delay를 키워도 지연 증가가 작은가?

정답

(a) 고부하: 요청이 빨리 쌓여 delay 거의 없이 preferred_batch에 도달 → delay를 키워도 지연 증가 작고 처리량은 큰 배치로 높음. 저부하: 요청이 드물어 delay까지 기다렸다 작은 배치 → delay를 키우면 그만큼 지연↑, 처리량 이득은 작음(배치가 별로 안 큼). (b) 고부하에선 큐에 요청이 이미 쌓여 있어 preferred_batch_size에 delay 경과 전에 도달 → 대기 시간이 거의 0. delay는 상한일 뿐 실제로는 안 쓰임. 그래서 delay는 주로 저부하 보호장치이고, 고부하에선 큰 배치를 거의 추가 비용 없이 얻는다.

연습 2 (인스턴스 × 배칭). instance=2, 동적 배칭 preferred=16일 때 유효 동시성과, 둘 중 하나만 키울 때의 한계를 설명하라.

정답

유효 동시성 ≈ 인스턴스(2) × 배치(16) = 32 요청 동시 처리(개략). 동적 배칭이 batch 16을 만들면 인스턴스1·인스턴스2가 각각 batch 16을 동시 실행. 한쪽만 키울 때: 배칭만 키우면(예 batch 64, instance 1) 큰 GEMM은 효율적이나 복사·간극이 오버랩 안 돼 GPU에 빈 시간이 남고, 단일 인스턴스라 그 빈 시간을 못 메움. 인스턴스만 키우면(instance 8, batch 1) 오버랩은 되나 각 실행이 batch=1이라 메모리 바운드 비효율(5.1.4). 둘을 곱으로 키워야 오버랩(인스턴스)과 산술 강도(배칭)를 함께 얻음 — Model Analyzer로 균형 탐색.

연습 3 (시퀀스 배칭). 스트리밍 음성 인식 모델이 같은 발화의 연속 오디오 청크 간 상태를 유지한다. 동적 배칭으로 서빙하면 무엇이 깨지고, 시퀀스 배칭이 어떻게 해결하나?

정답

동적 배칭으로 서빙하면: 같은 발화의 청크들이 임의의 인스턴스·임의의 배치로 흩어져 처리되어, 모델이 유지하던 상태(이전 청크 문맥)가 그 인스턴스에 없거나 순서가 뒤섞임 → 인식 결과 붕괴. 시퀀스 배칭 해결: 클라이언트가 같은 발화의 청크에 같은 correlation ID를 붙이면 Triton이 그것들을 같은 인스턴스·같은 슬롯으로 라우팅해 상태를 보존, START 플래그로 새 발화에서 상태 초기화·END로 정리. 스케줄링은 direct(슬롯 고정) 또는 oldest. 즉 같은 시퀀스를 같은 곳으로 순서대로 보내 상태·순서를 지킨다.

연습 4 (세 배칭 구분). (a) 이미지 분류, (b) 자기회귀 LLM 챗, (c) 세션 상태를 가진 스트리밍 모델 — 각각 어떤 배칭이 맞고 왜인지, 그리고 어디(스케줄러 vs 백엔드)서 일어나는지 답하라.

정답

(a) 동적 배칭(Triton 스케줄러) — 무상태 단발 forward라 요청을 자유롭게 모아 큰 배치로 처리량↑. (b) 인플라이트 배칭(tensorrtllm/vllm 백엔드) — 토큰을 수백 번 생성하고 요청이 제각각 끝나므로 요청 단위가 아니라 스텝 단위로 batch 재구성(5.8). Triton은 큐·스트리밍·메트릭만, 배칭은 백엔드. (c) 시퀀스 배칭(Triton 스케줄러) — 상태 유지라 correlation ID로 같은 세션을 같은 인스턴스로. 핵심: 무상태=동적, 상태=시퀀스, 자기회귀 LLM=인플라이트. 동적/시퀀스는 Triton 스케줄러, 인플라이트는 LLM 백엔드 내부.

연습 5 (혼합 트래픽). 한 서버가 대화형(엄격 p99)과 배치 임베딩(처리량 우선)을 동시에 받는다. 동적 배칭·우선순위·큐 정책을 어떻게 구성할지 설계하라.

정답

① 우선순위 레벨: 대화형을 높은 우선순위, 배치 임베딩을 낮은 우선순위로 — 대화형 요청이 배치 작업에 밀리지 않게 먼저 디스패치. ② 대화형 max_queue_delay 작게(SLA 예산 안, 예 수백 µs) — 지연 우선. 배치 임베딩 max_queue_delay 크게 — 처리량 우선(큰 배치 형성). ③ 큐 정책: 대화형 큐에 timeout/REJECT를 둬 과부하 시 SLA 보호(오래 기다린 요청 거부, goodput). max_queue_size로 폭주 방지. ④ 가능하면 모델/인스턴스 분리(또는 MIG/MPS, 7.5)로 두 클래스가 자원을 다투지 않게. 검증: 혼합 부하로 두 클래스의 p99·처리량을 각각 측정해 SLA 충족 확인.

5.13.12 요약 · 다음 장

  • 동적 배칭은 서버가 개별 요청을 잠깐 모아 큰 배치로 실행해 처리량을 높인다. 손잡이는 preferred_batch_size·max_queue_delay, 트레이드오프는 지연↔처리량 — 고부하에선 delay 거의 안 들고 효율적, delay는 저부하 보호장치.
  • 동적 배칭과 인스턴스(5.12)는 함께: 유효 동시성 ≈ 인스턴스 × 배치. Model Analyzer로 함께 스윕.
  • 시퀀스 배칭은 상태 있는 모델용 — correlation ID·start/end로 같은 세션을 같은 인스턴스로 라우팅(direct/oldest).
  • 세 배칭 구분: 동적(무상태 단발·스케줄러)·시퀀스(상태·스케줄러)·인플라이트(자기회귀 LLM·백엔드, 5.8). LLM엔 동적 배칭이 아니라 인플라이트.
  • 우선순위·큐 정책으로 혼합 트래픽의 SLA 보호. max_queue_delay는 SLA 예산 안에서 — 처리량을 위해 지연을 무한정 내주지 않는다(goodput).
  • 정직: 일부 옵션 버전 의존, 모든 수치 실측, 합성 부하의 한계 인지.

다음 장: 5.14 Triton IV — 앙상블·BLS·운영 지표. Triton 묶음의 마지막. 전처리→추론→후처리를 ensemble(정적 DAG) 로 묶고, 조건 분기·반복이 필요하면 BLS(Business Logic Scripting) 로 모델을 동적 호출하는 법, 그리고 Prometheus 메트릭으로 처리량·지연·큐·활용률을 관측하는 운영을 본다.