08. latency, throughput, concurrency
이 장의 핵심 연결: 01~07장에서 요청이 시스템을 지나는 모든 과정을 봤습니다. 이제 그것을 어떻게 관측·측정하는가를 엽니다. "throughput이 높다"는 문장을 시스템 관점에서 읽는 마지막 도구입니다.
다음 질문: latency와 throughput은 어떻게 다른가? TTFT·ITL/TPOT는 무엇을 재는가? "빠르다"는 주장을 검증하려면 어떤 측정 계약이 필요한가?
목표:
- latency·throughput·concurrency를 정의하고 서로의 관계(긴장)를 잡는다.
- LLM 특유의 지표(TTFT·ITL/TPOT·output tokens/s)를 정의한다.
- 어떤 지표든 측정 계약(무엇을·어떻게·어떤 분모로)을 쓰는 습관을 만든다.
먼저: "빠르다"는 무슨 뜻인가요?
"이 시스템이 빠르다"는 애매합니다. 두 가지 전혀 다른 것을 뜻할 수 있으니까요.
- 한 요청이 빨리 끝난다(지연이 짧다). — latency.
- 단위 시간에 많은 요청·토큰을 처리한다. — throughput.
이 둘은 자주 서로 긴장 관계에 있습니다. 요청을 많이 묶어 처리하면(배칭, 01·05장) throughput은 오르지만 개별 요청의 대기가 늘어 latency는 나빠질 수 있습니다. 그래서 "빠르다"를 말하려면 무엇을 재는지부터 정해야 합니다.
일상 비유: 카페의 두 가지 속도
- latency: 손님 한 명이 주문하고 음료를 받기까지의 시간.
- throughput: 한 시간에 카페가 내놓는 음료 잔 수.
바리스타가 주문을 몰아서 한꺼번에 만들면(배칭) 시간당 잔 수(throughput)는 늘지만, 먼저 온 손님은 몰아 만들 때까지 기다려(latency 증가) 짜증날 수 있습니다. 반대로 오는 즉시 하나씩 만들면 개별 손님은 빠르지만 시간당 총량은 줄 수 있습니다. 둘 다 최고로 만들 수는 없고, 워크로드에 맞춰 절충합니다.
정확한 정의: 세 지표와 LLM 특화 지표
기본 세 축
- latency(지연): 한 요청(또는 한 단계)이 걸리는 시간. 단위: 초.
- throughput(처리량): 단위 시간당 처리량. 단위: requests/s(초당 요청) 또는 output tokens/s(초당 출력 토큰).
- concurrency(동시성): 동시에 처리 중인 요청 수. 06장에서 봤듯 KV 캐시·메모리가 이를 제약합니다.
LLM 특화 지표 (01장의 prefill·decode에 대응)
CODE
TTFT (Time To First Token, 첫 토큰까지 시간) = 요청 도착 ~ 첫 출력 토큰이 나오기까지의 시간 → 주로 prefill 국면 + 대기(control path)를 반영 ITL (Inter-Token Latency, 토큰 간 지연) / TPOT (Time Per Output Token, 출력 토큰당 시간) = decode 국면에서 토큰 하나와 다음 토큰 사이의 시간 → 생성이 "얼마나 매끄럽게 이어지는가"를 반영 (ITL과 TPOT는 정의·집계 방식이 자료마다 다를 수 있어, 무엇을 뜻하는지 명시 필요) output tokens/s (초당 출력 토큰) = 시스템 관점의 throughput. 개별 요청용인지 전체 집계인지 반드시 구분
약어 풀이
- TTFT: 첫 토큰까지의 시간. 사용자가 "반응이 시작됐다"고 느끼는 시점.
- ITL / TPOT: 이어지는 토큰 사이의 간격. 값이 작을수록 생성이 매끄럽게 흐릅니다.
- goodput: throughput 중 실제로 쓸모 있는(조건을 만족한) 처리량만 센 것. 예를 들어 "TTFT가 기준 이내인 요청만" 센 처리량. 전체 throughput과 구분하려는 개념입니다.
시각적 직관: latency와 throughput의 긴장
flowchart TB
START[요청들이 도착]:::req --> CHOICE{배칭 정도}:::req
CHOICE -->|많이 묶음| BATCH[throughput ↑<br/>개별 latency ↑ 위험]:::result
CHOICE -->|적게 묶음| NOBATCH[개별 latency ↓<br/>throughput ↓ 위험]:::warn
BATCH --> LIMIT[동시성은 메모리·KV 캐시가 제약<br/>·06장·]:::mem
NOBATCH --> LIMIT
classDef req fill:#fef9c3,stroke:#ca8a04,color:#713f12
classDef result fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef warn fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef mem fill:#ede9fe,stroke:#7c3aed,color:#4c1d95
📌 핵심: throughput·latency·concurrency는 따로 최적화할 수 없습니다. 배칭을 늘리면 throughput↑·latency↑ 위험, concurrency는 메모리(06장)가 제약. 그래서 하나의 수치만 자랑하는 주장은 나머지를 숨기고 있을 수 있습니다.
이 장의 실천: 측정 계약(measurement contract) 쓰기
지표 수치는 어떻게 쟀는지 없이는 비교 불가입니다. 이 안내서는 모든 지표에 아래 계약을 붙이길 권합니다.
CODE
측정 계약 템플릿
─────────────────────────────
[무엇을] 어떤 지표인가? (TTFT? output tokens/s? 개별인가 전체 집계인가?)
[조건] 모델·정밀도(03장)·하드웨어·소프트웨어 버전은?
[워크로드] 입력 길이·출력 길이 분포는? 요청률(도착 속도)은?
[동시성] 동시 요청 수(concurrency)는 얼마로 고정/변화?
[분모] "초당 토큰"의 분모가 무엇인가?
(한 요청 기준? 전체 시스템 합산? 시간 창은?)
[집계] 평균인가 중앙값인가 p95/p99(상위 지연)인가?
[peak/measured] 이론 peak인가 실제 관측인가? (09장)
왜 분모가 중요한가: "이 시스템은 10,000 tokens/s"가 한 요청의 속도인지 전체 동시 요청 합산인지에 따라 뜻이 완전히 다릅니다. 분모를 안 밝히면 비교가 무의미합니다. concurrency가 다르면 같은 "tokens/s"도 다른 이야기입니다.
⚖️ 절충(tradeoff): 지표들 사이
CODE
throughput을 올리려 배칭을 키우면: 얻는 것: output tokens/s(집계) ↑, 가속기를 알차게 사용 치르는 것: 개별 요청 TTFT·latency ↑ 위험, 큰 KV 캐시 → 메모리 압박 latency(TTFT)를 줄이려 배칭을 줄이면: 얻는 것: 개별 요청 반응 빠름 치르는 것: throughput ↓, 가속기 유휴 가능 concurrency를 올리려면: 전제: 메모리·KV 캐시 여유(06장)가 있어야 함 치르는 것: 요청당 자원 축소 → 개별 latency 영향 가능 숨은 전제: "무엇을 우선하는가"(대화형이면 TTFT, 배치 처리면 throughput)에 따라 최적점이 다름. 목표 없이는 "빠르다"의 정의조차 못 세움.
⚠️ 흔한 오해
- "throughput이 높으면 좋은 시스템이다." — 개별 latency를 희생한 결과일 수 있습니다. 대화형 서비스라면 나쁜 경험일 수 있습니다. 무엇을 우선하는지에 달렸습니다.
- "tokens/s 하나면 비교 끝." — 분모(개별/집계)·concurrency·입출력 길이를 모르면 비교 불가입니다.
- "평균 latency가 낮으니 빠르다." — 상위 지연(p95/p99)이 나쁘면 일부 사용자는 크게 느립니다. 집계 방식을 봐야 합니다.
- "latency와 throughput은 같이 좋아진다." — 자주 긴장 관계입니다. 배칭이 대표적 예입니다.
조건 명시
| 내용 | |
|---|---|
| 성립하는 조건 | latency·throughput·concurrency의 정의, TTFT·ITL의 개념, 세 지표의 긴장 관계는 원리적으로 안정적입니다. |
| 성립하지 않는 결론 | "이 시스템이 저 시스템보다 빠르다"를 측정 계약 없이 단정할 수 없습니다. 지표는 조건·분모·집계에 따라 뒤바뀝니다. 특정 수치는 measured이며 조건을 반드시 밝혀야 하고, peak 수치를 실측처럼 써서도 안 됩니다(09장). |
🔬 증거 읽기: 지표 주장의 유형
- "이 설정에서 TTFT 중앙값 X초, 집계 throughput Y tokens/s" → measured (조건·분모·집계·동시성을 밝혀야 유효).
- "이론상 최대 Z tokens/s 가능" → peak/calculated이며, 실측과 구분해야 함.
- "그러므로 이 시스템이 더 빠르다" → 조건·분모가 다른 수치를 비교했다면 지지되지 않는 결론.
이 장에서 배운 것
- "빠르다"는 latency(한 요청)와 throughput(단위 시간당) 중 무엇인지 정해야 뜻이 선다.
- LLM 특화: TTFT(첫 토큰, prefill+대기), ITL/TPOT(토큰 간 간격, decode), output tokens/s.
- 세 지표는 긴장 관계 — 배칭이 throughput↑·latency↑, concurrency는 메모리(06장)가 제약.
- 모든 수치에 측정 계약(무엇을·조건·워크로드·분모·집계·peak여부)을 붙여야 비교 가능.
- 하나의 수치만 자랑하는 주장은 나머지를 숨겼을 수 있다.
🔎 이 장의 '지지하지 않는 결론'
- 이 장으로 측정 계약 없는 "빠르다" 비교를 지지할 수 없다. 분모·조건이 다르면 같은 tokens/s도 다른 이야기다.
- "throughput이 높으니 좋은 시스템"을 단정할 수 없다. 목표(대화형/배치)에 따라 평가가 달라진다.
✍️ 확인 문제
- 배칭을 키웠더니 output tokens/s(집계)는 올랐는데 사용자들이 "반응이 느리다"고 합니다. 어떤 지표가 나빠졌을 가능성이 큰가요? 왜죠?
- "이 시스템은 10,000 tokens/s"라는 주장을 검증하려면 최소한 어떤 항목들을 함께 물어야 하나요(측정 계약에서 셋 이상)?
- [조건 묻기 / peak-measured] 두 시스템의 tokens/s를 비교하려는데, 하나는 concurrency 1, 다른 하나는 concurrency 64에서 잰 값입니다. 이 비교가 왜 부당한가요? 그리고 한쪽이 "이론 peak"이면 어떤 추가 문제가 생기나요?
다음 장: 09 · 사양표와 시스템 구성도 읽기 — 지금까지의 모든 태도를 종합합니다. 가상의 스펙 표에서 peak·measured·calculated·inferred를 분류하고, 지지되지 않는 결론을 직접 표시합니다.