추론 · 워크로드 · 2026-09-19
추론 성능 지표: 대기 시간과 처리량
한 요청의 기다림과 서버 전체 처리량을 측정하고, 평균에 가려진 출력 공백과 요청별 지연을 살펴본 뒤 지연 목표를 만족한 처리량인 Goodput으로 연결합니다.
앞선 글에서는 요청을 배치에 넣어 실행하고, KV 캐시를 관리하며, 공간이 부족하면 일부 요청을 중단했다가 재개하는 과정을 살펴봤습니다. 이제 이 과정이 얼마나 잘 동작하는지 판단할 기준이 필요합니다. 한 요청이 빨리 끝났다고 서버가 많은 요청을 처리한 것은 아니고, 많은 요청을 완료했다고 모든 사용자가 짧게 기다린 것도 아닙니다.
이번 글에서는 한 사용자가 겪는 기다림을 나누어 측정한 뒤, 서버 전체가 일정 시간 동안 처리한 양을 보겠습니다. 이어서 평균에 가려질 수 있는 출력 공백과 요청별 지연을 살펴보고, 지연 목표를 지키면서 처리한 양을 뜻하는 Goodput으로 연결하겠습니다. 그림의 수치는 원리를 설명하기 위한 예시이며 실제 성능 측정 결과는 아닙니다.
한 요청의 지연 시간
사용자가 요청을 보내고 답변을 받는 과정을 생각해 보겠습니다. 처음에는 아무 출력도 없이 기다립니다. 첫 토큰을 받은 뒤에는 다음 토큰들이 차례로 도착하고, 마지막에는 요청이 끝납니다. 같은 요청 안에서도 첫 답변을 기다리는 시간과 답변이 이어지는 간격은 구별할 수 있습니다.
여기서는 클라이언트가 요청을 제출하고 출력을 받는 시각을 기준으로 삼겠습니다. 설명을 위해 출력 묶음인 청크 하나에 토큰 하나가 담겨 도착한다고 가정합니다. 그림 1에서는 0ms에 요청을 보내고, 100ms·130ms·170ms에 토큰을 받은 뒤 180ms에 요청 종료를 확인합니다.

TTFT(Time to First Token)는 요청을 제출한 뒤 첫 출력 토큰을 받기까지 걸린 시간입니다. 그림에서는 100 − 0 = 100ms입니다. 사용자가 첫 답변을 기다린 시간이므로 큐 대기, 입력 처리, 출력 전달 등이 함께 들어갈 수 있습니다. 따라서 TTFT 전체를 Prefill의 계산 시간으로 해석해서는 안 됩니다.
ITL(Inter-Token Latency)은 연속된 출력 토큰 사이의 간격입니다. x0에서 x1까지는 130 − 100 = 30ms, x1에서 x2까지는 170 − 130 = 40ms입니다. 한 요청에서 여러 값이 나오며, 각각 생성 중 어느 구간에서 오래 기다렸는지를 보여줍니다.
TPOT(Time per Output Token)는 이 글에서는 첫 토큰 이후 출력 간격의 요청별 평균으로 정의하겠습니다. 출력 토큰이 3개이면 그 사이 간격은 2개이므로, 그림의 TPOT는 (30 + 40) / 2 = 35ms/token입니다. 일반적으로 출력 토큰이 N개일 때 다음처럼 계산합니다.
TPOT = (마지막 토큰 수신 시각 − 첫 토큰 수신 시각) / (N − 1)
첫 토큰까지의 기다림은 이 평균에서 제외합니다. 토큰을 하나만 받았다면 토큰 사이 간격 자체가 없으므로 이 정의의 TPOT는 계산할 수 없습니다. 이런 요청을 0ms로 넣으면 평균이 왜곡될 수 있어 별도로 처리해야 합니다.
전체 요청 시간(E2E latency)은 요청 제출부터 종료를 확인할 때까지 걸린 시간입니다. 그림에서는 180ms입니다. 마지막 토큰은 170ms에 왔지만 종료는 180ms에 확인했으므로 두 시점에 차이가 있습니다. 측정 도구가 마지막 토큰과 종료 중 어디까지 재는지에 따라 값이 달라질 수 있습니다. 특히 TPOT 계산에 전체 요청 시간을 사용하는 도구라면 그 시간의 끝점도 함께 확인해야 합니다.
이렇게 정의한 값은 사용자가 관측한 지연입니다. 모델이 토큰을 선택한 뒤에는 텍스트 변환, 출력 묶음 구성과 전송이 이어지므로 내부 생성 시각과 수신 시각은 다를 수 있습니다. 실제로 청크 하나에 여러 토큰이 담기면 청크 도착 간격만으로 개별 토큰의 간격을 알 수도 없습니다. 내부 실행 시간을 재는 경우와 클라이언트 수신 시간을 재는 경우를 구별하고, 어떤 경계를 사용했는지 밝혀야 합니다.
여러 요청의 처리량
한 요청의 지연을 알았다면, 이제 서버 전체로 시선을 넓혀 보겠습니다. 서버는 여러 요청을 함께 처리합니다. 각 요청의 출력이 일정한 속도로 도착하더라도 동시에 진행하는 요청 수에 따라 전체가 만들어 내는 출력의 양은 달라집니다.
처리량(Throughput)은 일정 시간 동안 처리한 작업량입니다. 완료한 요청을 세면 요청 처리량이 되고, 출력 토큰을 세면 출력 토큰 처리량이 됩니다. 둘은 세는 대상과 단위가 다르므로 구별해서 표시해야 합니다.
그림 2는 앞선 180ms 예시와 별개의 10초 관측 구간입니다. 이 구간 안에서 요청 A·B·C가 시작하고 모두 완료됩니다. 빈 원은 제출, 채워진 점은 출력 토큰 수신, 체크 표시는 완료입니다.

체크 표시를 세면 완료 요청이 3개입니다. 따라서 요청 처리량은 3 / 10 = 0.3requests/s입니다. 출력 점은 A에 3개, B에 5개, C에 2개로 총 10개이므로 출력 토큰 처리량은 10 / 10 = 1token/s입니다. 같은 실행을 보고 있지만 분자에 무엇을 넣느냐에 따라 다른 지표가 됩니다.
한 요청의 TPOT를 뒤집어 얻은 생성 속도는 이 서버 전체 처리량과 같지 않습니다. 그림처럼 다른 요청들도 같은 시간 동안 출력을 만들기 때문입니다. 또한 여기서는 출력 토큰만 셌습니다. 입력과 출력을 합친 토큰 처리량을 제시한다면 그 사실을 함께 적어야 합니다.
이 예시에서는 모든 요청과 출력이 관측 구간 안에 들어오도록 했습니다. 실제 측정에서는 구간 시작 전에 들어온 요청이나 구간이 끝나도 실행 중인 요청이 있을 수 있습니다. 따라서 완료 요청 수를 세는지, 구간 안에 도착한 출력 토큰을 세는지와 함께 관측 시간의 시작과 끝도 정해야 합니다.
평균에 가려진 출력 공백
여기까지는 한 요청이 얼마나 기다렸는지와 서버가 얼마나 처리했는지를 나누어 봤습니다. 이제 그 결과를 평균 하나로 요약하면 무엇을 놓칠 수 있는지 살펴보겠습니다. 먼저 한 요청의 출력 간격으로 돌아가 보겠습니다.
TPOT가 35ms/token이면 출력이 항상 35ms 간격으로 도착할까요? 그렇지는 않습니다. 그림 3에서는 두 경우 모두 첫 토큰부터 마지막 토큰까지 70ms가 걸립니다. 하지만 위쪽은 35ms씩 두 번 기다리고, 아래쪽은 5ms를 기다린 뒤 65ms를 기다립니다.

두 경우 모두 간격의 합이 70ms이고 간격 수가 2개이므로 TPOT는 같습니다. 그러나 아래쪽의 사용자는 x1을 받은 뒤 다음 출력이 오지 않는 긴 구간을 겪습니다. 평균 생성 속도가 같다는 사실만으로 출력이 꾸준히 이어졌다고 판단할 수는 없습니다.
앞선 Preemption 글에서 중단된 요청이 재개를 기다리고 KV를 복구하는 동안 새 출력이 없었던 상황을 떠올릴 수 있습니다. 이런 긴 간격도 다른 짧은 간격과 함께 평균을 내면 영향이 작아 보일 수 있습니다. 요청별 TPOT와 함께 실제 ITL을 살펴보면 평균만으로 드러나지 않던 멈춤을 볼 수 있습니다.
요청마다 다른 지연과 백분위수
이번에는 여러 요청 사이의 차이를 보겠습니다. 대부분의 요청이 빨리 첫 토큰을 받더라도 일부 요청은 오래 기다릴 수 있습니다. 요청 전체의 평균 TTFT만 보고 있으면 이런 사용자가 얼마나 오래 기다렸는지 알기 어렵습니다.
그림 4는 20개 요청의 TTFT를 짧은 순서로 정렬한 것입니다. 각 막대는 한 요청이며, 높이는 첫 토큰까지 기다린 시간입니다. 오른쪽의 몇몇 요청은 다른 요청보다 훨씬 긴 지연을 보입니다. 이런 분포의 긴 쪽 끝을 꼬리라고 부르며, 그 구간의 지연을 꼬리 지연이라고 합니다.

백분위수(Percentile)는 이 정렬된 값에서 특정 비율에 해당하는 위치를 살펴보는 방법입니다. p50은 50%, p95는 95%에 해당하는 지점을 봅니다. 이 예시에서는 순위 = 올림(비율 × 요청 수) 규칙을 사용하겠습니다. p50에는 0.50, p95에는 0.95를 넣습니다.
요청이 20개이므로 p50은 10번째 값인 190ms, p95는 19번째 값인 600ms입니다. 이 예시의 요청 10개는 TTFT가 190ms 이하이고, 19개는 600ms 이하입니다. 남은 한 요청은 1000ms를 기다립니다. 따라서 p95가 600ms라는 말이 모든 요청이 600ms 안에 첫 토큰을 받았다는 뜻은 아닙니다.
백분위수를 구하는 방식에는 값 사이를 보간하는 방법도 있습니다. 같은 표본이라도 도구의 계산 규칙에 따라 결과가 달라질 수 있으므로 비교할 때는 같은 규칙을 사용해야 합니다. 그림의 20개 표본은 원리를 보여주기 위한 것이며, 실제로 p99처럼 극단적인 꼬리를 추정하려면 충분한 표본이 필요합니다.
무엇을 모아 만든 분포인지도 중요합니다. 요청별 TTFT를 모은 분포, 요청별 평균 TPOT를 모은 분포, 모든 토큰 간격을 모은 ITL 분포는 서로 다릅니다. 특히 ITL을 모두 모으면 긴 출력을 만든 요청이 더 많은 표본을 기여합니다. ‘p99’라는 표기만 비교하지 말고 어떤 지표의 어떤 표본을 정렬했는지도 확인해야 합니다.
지연 목표를 만족한 처리량
이제 지연과 처리량을 함께 보겠습니다. 서버가 많은 요청을 완료했더라도 첫 답변이 너무 늦거나 생성이 지나치게 느리면 사용자가 기대한 응답을 제공했다고 보기 어렵습니다. 서비스에 필요한 지연 목표를 정하면, 완료한 요청 중 그 목표를 지킨 요청이 얼마나 되는지도 셀 수 있습니다.
그림 5에서는 각 요청의 TTFT가 1초 이하이고 TPOT가 50ms/token 이하라는 두 조건을 정했습니다. 이 수치는 설명을 위한 목표이며 모든 서비스에 적합한 기준이라는 뜻은 아닙니다. 요청 하나가 두 조건을 모두 만족해야 통과한 것으로 셉니다.

관측 시간 10초 동안 10개 요청이 성공적으로 완료됐으므로 전체 요청 처리량은 10 / 10 = 1request/s입니다. 하지만 목표를 모두 만족한 요청은 8개입니다. 나머지 2개도 완료는 됐지만, 하나는 첫 응답이 늦었고 다른 하나는 평균 출력 간격이 목표를 넘었습니다.
이 글에서는 요청 기준 Goodput을 지연 목표를 모두 충족하며 성공 완료한 요청 수를 관측 시간으로 나눈 값으로 정의하겠습니다. 그림에서는 8 / 10 = 0.8requests/s입니다. Inference Perf의 Goodput 문서도 이처럼 성공 완료와 지정한 조건을 함께 충족한 요청을 세는 방식을 설명합니다.
목표 달성률은 별개의 값입니다. 이 예시에서 완료한 요청 중 목표를 만족한 비율은 8 / 10 = 80%입니다. 달성률의 분모는 요청 수이고, Goodput의 분모는 시간입니다. 전체 처리량, 목표 달성률, Goodput을 함께 보면 얼마나 많이 처리했고 그중 얼마나 목표를 지켰는지 구별할 수 있습니다.
무엇을 통과 조건으로 삼았는지도 결과와 함께 밝혀야 합니다. 여기서는 TTFT와 요청별 평균 TPOT를 사용했습니다. 앞서 본 것처럼 평균 TPOT가 목표 안에 들어와도 한 번의 긴 출력 공백은 남을 수 있습니다. 모든 토큰 간격에 상한을 두는 조건은 이 그림의 조건과 다릅니다. Goodput이라는 이름만으로 어떤 사용자 경험까지 충족했는지 판단할 수는 없습니다.
같은 조건에서 최적화 비교하기
이 지표들로 최적화 전후를 비교하려면 처리하는 요청과 부하도 맞춰야 합니다. 짧은 입력을 처리하는 경우와 긴 입력을 처리하는 경우는 Prefill 비용이 다르고, 출력 길이가 달라지면 Decode 횟수와 KV 보유 기간도 달라집니다. 같은 모델이라도 요청이 드문 경우와 한꺼번에 몰리는 경우의 기다림은 다릅니다.
최소한 입력·출력 길이, 요청 도착률과 동시성 제한, 모델과 하드웨어, 측정 구간을 함께 기록해야 합니다. 도착률은 초당 새로 들어오는 요청 수이고, 동시성은 아직 끝나지 않은 요청 수입니다. 일정 개수의 요청만 유지하며 완료될 때마다 다음 요청을 보내는 시험에서는 응답이 느려질수록 실제 도착률도 내려갈 수 있습니다. 정해진 속도로 계속 요청을 보내는 시험과 같은 부하라고 볼 수는 없습니다.
성공 완료한 요청뿐 아니라 거절·실패·취소된 요청을 어떻게 처리했는지도 남겨야 합니다. 요청을 적게 받아 목표 달성 비율만 높인 결과와, 같은 제공 부하에서 목표를 만족한 실제 처리량을 늘린 결과를 구별하기 위해서입니다.
이제 이후의 최적화를 볼 때는 어떤 기다림을 줄였는지, 전체 처리량은 어떻게 바뀌었는지, 일부 요청의 긴 지연은 남아 있는지, 정해진 목표를 만족한 처리량이 늘었는지를 함께 물을 수 있습니다. 뒤에서 다룰 스케줄링과 진입 제어도 이런 기준을 바탕으로 요청에 자원을 배분하는 방법으로 이어집니다.