2026년 10월 22일 · INFINITIX 제품팀

들어가며: “이 서버는 동시에 몇 명의 사용자에게 서비스를 제공할 수 있을까?”

한 팀이 GPU 서버에 오픈소스 대규모 언어 모델(LLM)을 배포했다고 가정해 보자. Curl로 프롬프트(Prompt)를 전송하니 1초 남짓 지나 응답 텍스트가 출력되기 시작한다. 언뜻 보기에는 충분히 빠르다.

하지만 ‘빠르게 보인다’는 사실만으로는 다음 질문에 답할 수 없다.

– 시스템은 동시에 10명의 요청을 처리할 수 있을까, 아니면 200명까지 처리할 수 있을까?

– 사용자가 첫 번째 응답 텍스트를 몇 초 이내에 확인해야 적절한 서비스 품질이라고 할 수 있을까?

– 스트리밍 방식으로 응답을 표시할 때 어느 정도의 출력 속도가 확보되어야 자연스럽고 원활하게 느껴질까?

이러한 질문에 답하기 위해 필요한 것이 바로 LLM 벤치마크(LLM Benchmark), 즉 성능 기준 테스트다.

이 글에서는 벤치마크 도구의 설치 방법보다는 테스트의 기본 원리에 집중한다. 어떤 지표를 측정해야 하는지, 왜 하나의 수치만으로 성능을 판단해서는 안 되는지, 부하를 어떤 방식으로 발생시켜야 하는지, 그리고 어떤 요소가 측정 결과를 왜곡할 수 있는지를 살펴본다.

또한 NVIDIA가 오픈소스로 공개한 AIPerf를 예시로 활용해 이러한 개념이 실제 벤치마크 도구에서 어떻게 구현되는지 설명한다.

1. LLM 벤치마크란 무엇인가?

LLM 벤치마크라는 용어는 일반적으로 두 가지 의미로 사용된다.

첫 번째는 모델의 능력을 평가하는 벤치마크다. MMLU, HumanEval과 같은 평가 데이터셋을 통해 모델이 질문에 얼마나 정확하게 답하는지를 측정하며, 하드웨어 성능 자체를 평가하는 것은 아니다.

두 번째는 추론 성능 벤치마크(Inference Performance Benchmark)다. 동일한 모델을 특정 하드웨어와 추론 엔진에 배포했을 때 응답 속도가 얼마나 빠른지, 동시에 몇 명의 사용자에게 서비스를 제공할 수 있는지를 평가한다.

이 글에서 다루는 것은 두 번째 유형이다.

다만 LLM 추론 성능 테스트는 일반적인 HTTP 부하 테스트와 동일한 방식으로 접근해서는 안 된다. LLM 추론에는 기존 API 서비스와 구별되는 몇 가지 중요한 특성이 있기 때문이다.

첫째, 응답이 토큰 단위로 스트리밍된다.

첫 번째 토큰이 언제 도착하는지(Time to First Token, TTFT), 이후 토큰 사이의 간격이 얼마나 되는지(Inter-Token Latency, ITL), 전체 응답이 언제 완료되는지(End-to-End Request Latency, E2E Latency)는 서로 다른 시간 지표다.

둘째, 요청마다 처리해야 하는 작업량이 다르다.

모델은 서로 다른 시점에 생성을 종료할 수 있으며, 출력이 길어질수록 일반적으로 GPU를 점유하는 시간도 길어진다. 두 테스트의 출력 길이 분포가 다르다면 처리량을 직접 비교하기 어렵다.

셋째, 연산은 Prefill과 Decode의 두 단계로 나뉜다.

Prefill은 입력 프롬프트 전체를 처리하는 단계이고, Decode는 출력 토큰을 순차적으로 생성하는 단계다. 두 단계는 요구하는 자원과 성능 병목 요인이 서로 다르다.

넷째, 서로 다른 요청이 상호 영향을 미친다.

추론 엔진은 연속 배치 처리(Continuous Batching)를 통해 여러 요청의 Decode 작업을 묶어 스케줄링할 수 있다. 따라서 개별 요청의 토큰 생성 속도는 동시에 실행 중인 다른 요청의 영향을 받는다.

다섯째, GPU 메모리가 먼저 병목이 될 수 있다.

아직 완료되지 않은 요청은 각각 KV Cache를 점유하며, 필요한 메모리 용량은 시퀀스 길이에 따라 증가한다. 메모리 사용량이 한계에 가까워지거나 한계에 도달하면 지연 시간이 점진적으로 증가하는 것이 아니라 갑자기 크게 악화될 수 있다.

따라서 벤치마크를 수행하기 전에는 최소한 다음 다섯 가지를 명확히 정의해야 한다.

1. 테스트 대상: API 서비스 엔드포인트(Endpoint), 모델, 스트리밍 활성화 여부

2. 워크로드: 프롬프트 출처, 입력 시퀀스 길이(Input Sequence Length, ISL), 출력 시퀀스 길이(Output Sequence Length, OSL)

3. 부하 모델: 동시 요청 수와 요청 전송 간격

4. 측정 지표

5. 판정 기준: 서비스 수준 목표(Service Level Objective, SLO)

벤치마크를 하나의 통제된 실험으로 생각하고 한 번에 하나의 변수만 변경해야 결과가 달라진 원인을 쉽게 파악할 수 있다.

2. 왜 하나의 숫자만으로 성능을 판단해서는 안 될까?

2.1 하나의 시간축에서 측정하는 네 가지 핵심 지표

모든 지표는 동일한 시간축을 기준으로 계산된다.

t₀는 요청을 전송한 시점이고, t₁은 첫 번째 토큰을 수신한 시점이다. 이후 토큰이 순차적으로 도착하며, tₙ은 마지막 토큰을 수신한 시점이다.

그림 1. 클라이언트(Client)에서 관찰한 스트리밍 요청의 시간축과 네 가지 핵심 지표에 해당하는 시간 구간

TTFT(Time to First Token) = t₁ − t₀

TTFT는 첫 번째 토큰을 수신하기까지 걸린 시간이다. 이 시간에는 네트워크 왕복 시간, 서버의 요청 대기시간, 전체 프롬프트를 처리하는 Prefill 시간이 포함된다. 사용자가 ‘시스템이 응답하고 있다’고 느끼는 시점을 나타내며, 프롬프트 길이에 민감하다. 또한 동시 요청 수가 증가할 때 가장 먼저 악화되는 지표 중 하나다.

ITL(Inter-Token Latency) = (Request Latency − TTFT) ÷ (OSL − 1)

ITL은 첫 번째 토큰을 받은 이후 다음 토큰을 수신하기까지의 평균 간격이다. Decode 단계에서 출력이 얼마나 자연스럽게 이어지는지를 나타낸다. 업계에서는 TPOT(Time Per Output Token)라는 관련 용어도 널리 사용한다. 분모가 OSL이 아닌 OSL − 1인 이유는 첫 번째 토큰을 기다리는 시간이 이미 TTFT에 포함되어 있기 때문이다.

Request Latency = tₙ − t₀

요청을 전송한 시점부터 전체 응답을 완료할 때까지 걸린 총 시간이다. 전체 응답 시간이 동일하더라도 사용자 경험은 크게 다를 수 있다. 한 요청은 첫 응답까지 오래 기다리지만 이후 빠르게 출력될 수 있고, 다른 요청은 첫 응답이 빠르지만 이후 출력 속도가 느릴 수 있다. 따라서 전체 응답 시간은 TTFT와 ITL을 함께 확인해야 한다.

사용자별 출력 속도 = 1 ÷ ITL

개별 사용자가 1초에 수신할 수 있는 대략적인 토큰 수를 의미한다. 출력 속도가 초당 한 자릿수 토큰 수준으로 떨어지면 사용자는 일반적으로 응답이 느리다고 체감하게 된다.

2.2 처리량(Throughput): 시스템 전체의 생산 능력

Benchmark Duration

첫 번째 요청을 전송한 시점부터 마지막 응답을 수신할 때까지의 전체 시간이다. 처리량 계산 시 이 시간을 분모로 사용한다.

Output Token Throughput = 전체 출력 토큰 수 ÷ Duration

시스템 전체가 1초 동안 생성하는 출력 토큰 수를 의미하며, 단위는 TPS(Tokens Per Second)다.

Request Throughput = 완료된 요청 수 ÷ Duration

시스템이 1초 동안 완료하는 요청 수를 의미하며, 단위는 RPS(Requests Per Second)이다. 요청 처리량과 토큰 처리량 사이의 관계는 요청당 평균 출력 길이에 따라 달라진다.

가장 혼동하기 쉬운 예시는 다음 두 표현이다.

– 사용자 한 명이 초당 20개의 토큰을 수신한다.

– 시스템 전체가 초당 2,000개의 토큰을 생성한다.

두 수치는 서로 다른 의미를 가진다. 동시 요청 수가 증가하면 Batching을 통해 시스템 전체 처리량은 증가할 수 있다. 반면 개별 Decode 단계의 처리 시간이 늘어나 사용자 한 명이 체감하는 출력 속도는 감소할 수 있다.

따라서 tokens/s라는 단위를 볼 때는 사용자별 속도인지 시스템 전체 처리량인지 먼저 확인해야 한다.

2.3 왜 평균값만 확인해서는 안 될까?

지연 시간 분포는 일반적으로 오른쪽으로 치우친 긴 꼬리(Long Tail) 형태를 보인다. 대부분의 요청은 빠르게 완료되지만, 일부 요청은 대기열이나 메모리 압박으로 인해 몇 배나 더 오래 걸릴 수 있다. 평균값은 이러한 느린 요청의 영향을 받지만, 최악에 가까운 요청들이 얼마나 느린지 명확하게 보여주지는 못한다. AIPerf 방법론 문서의 예시에서는 p99 지연 시간이 평균보다 70% 이상 높게 나타났다. 

일반적으로 백분위 지표는 다음과 같이 해석한다.

– p50: 일반적인 요청의 지연시간을 파악하는 지표

– p95: 전체 요청의 95%가 이 값 이하의 지연시간을 보임

– p99: 전체 요청의 99%가 이 값 이하의 지연시간을 보이며, 가장 느린 1%는 이보다 오래 걸림

SLO는 보통 p95 또는 p99를 기준으로 설정한다. 따라서 좋은 벤치마크 도구는 요청별 원본 데이터를 보관해 테스트 이후에도 다양한 백분위수(Percentile)를 다시 계산할 수 있어야 한다.

2.4 Goodput: 속도와 서비스 품질을 동시에 측정하다

지연 시간과 처리량은 서로 상충하는 경우가 많다. ‘시스템이 몇 명의 사용자를 감당할 수 있는가?’라는 질문에 답하려면 두 지표를 동일한 기준으로 평가해야 한다. Goodput = 모든 SLO 조건을 충족한 요청 수 ÷ Duration 예를 들어 다음과 같은 SLO를 설정할 수 있다.

– TTFT < 500ms

– ITL < 50ms

두 조건을 모두 충족한 요청만 Goodput 계산에 포함된다.

따라서 Goodput은 Request Throughput보다 클 수 없다. 용량 계획에서 어떤 지표를 선택하느냐에 따라 결과는 크게 달라질 수 있다. AIPerf 문서의 예시에서는 Throughput을 기준으로 계산하면 서버 38대가 필요하지만, Goodput을 기준으로 계산하면 135대가 필요한 것으로 나타났다. 후자는 단순히 작업을 완료하는 것뿐만 아니라 사용자 경험도 요구 수준을 충족하는 용량을 의미한다.

지표계산식의미 및 주요 활용
TTFTt₁ − t₀사용자가 시스템의 응답을 체감하는 속도
ITL(TPOT)(request latency − TTFT) ÷ (OSL − 1)스트리밍 출력의 원활함
Request latencytₙ − t₀배치 작업 및 Agent 워크플로의 완료 시간
Output token throughputΣ OSL ÷ duration운영비용 및 GPU 활용률
GoodputSLO 충족 요청 수 ÷ Duration용량 계획 및 서비스 품질

2.5 스트리밍을 비활성화하면 TTFT와 ITL을 측정할 수 없는 이유

스트리밍을 사용하지 않으면 서버는 전체 응답 생성이 완료된 후 결과를 한 번에 반환한다. 이 경우 클라이언트에서 관찰할 수 있는 시간축은 t₀와 tₙ뿐이다. 첫 번째 토큰과 마지막 토큰이 전체 응답과 함께 도착하므로 TTFT는 Request Latency와 같아지고, ITL 계산도 의미를 잃는다. 따라서 TTFT, ITL, 사용자별 출력 속도는 스트리밍 출력에서만 유효한 지표다. AIPerf 역시 이를 Streaming-only 지표로 구분한다. 반대로 백엔드 배치 요약 작업처럼 원래 스트리밍을 사용하지 않는 서비스라면 이러한 지표를 반드시 확인할 필요는 없다. 

측정 지표는 제품의 실제 사용 방식에 맞춰 선택해야 한다.

또한 측정 결과를 왜곡할 수 있는 두 가지 설정에 주의해야 한다. 첫째, 출력 길이가 제대로 제어되지 않는 경우다. 모델이 종료 토큰(EOS)을 조기에 생성해 출력을 중단하면 실제 OSL이 예상보다 짧아지고, 처리량과 ITL에도 영향을 미친다. 보다 엄격한 테스트에서는 ignore_eos, min_tokens 등의 매개변수를 활용해 설정한 길이만큼 토큰이 생성되도록 한다. 다만 일부 서비스는 여전히 max_tokens라는 매개변수 이름을 사용하며, 지원하지 않는 매개변수를 오류 없이 무시할 수도 있다는 점에 주의해야 한다. 둘째, Tokenizer 설정이 잘못된 경우다. 클라이언트는 Tokenizer를 사용해 ISL과 OSL을 계산한다. 사용하는 Tokenizer가 실제 모델과 일치하지 않으면 토큰 단위로 측정하는 모든 지표에 오차가 발생할 수 있다.

3. 부하 모델: 부하를 발생시키는 방식이 측정 결과를 결정한다

부하 모델(Load Model)은 단순히 부하의 크기를 설정하는 것이 아니다. 더 중요한 것은 요청을 언제, 어떤 규칙에 따라 전송할 것인지를 결정하는 메커니즘이다.

그림 2. 폐쇄형 루프(고정 동시 요청 수)와 개방형 루프(고정 요청 도착률) 부하 모델의 차이

Concurrency(고정 동시 요청 수, 폐쇄형 루프)

N명의 가상 사용자를 시뮬레이션한다. 각 사용자는 요청 하나를 전송하고 전체 응답을 받은 뒤 다음 요청을 전송한다. 따라서 어느 시점이든 완료되지 않은 요청 수는 N을 초과하지 않는다. 이 방식에는 피드백 효과가 있다. 시스템이 느려질수록 다음 요청도 늦게 전송되므로 전체 요청 전송 속도가 자동으로 감소한다. ‘N명의 사용자가 지속적으로 서비스를 사용할 때’ 시스템이 유지할 수 있는 정상 상태의 처리 용량을 측정하는 데 적합하다. 결과가 비교적 안정적이고 재현하기 쉽지만, 갑작스러운 트래픽 증가를 시뮬레이션하기에는 상대적으로 완만한 부하를 발생시킨다.

Request Rate(고정 요청 도착률, 개방형 루프)

초당 λ개의 요청을 일정한 스케줄에 따라 전송한다. 이전 요청의 완료 여부와 관계없이 다음 요청이 전송된다. 시스템의 처리 속도가 요청 도착 속도를 따라가지 못하면 완료되지 않은 요청이 계속 누적되고 지연 시간도 급격히 증가할 수 있다. 이 방식은 일정한 속도로 요청이 도착하는 실제 트래픽 환경에 더 가깝다. 따라서 ‘예상되는 최대 트래픽 상황에서도 지연 시간이 SLO를 충족하는가?’를 검증하는 데 적합하다.

다만 λ 설정에 매우 민감하다. 시스템이 감당할 수 있는 처리 용량보다 조금 낮은 요청률에서는 문제가 없을 수 있지만, 이를 약간 초과하면 대기열이 지속적으로 누적될 수 있다.

두 방식은 서로 다른 질문에 답한다.

‘최대 몇 명의 사용자에게 동시에 서비스를 제공할 수 있는가?’라는 질문에는 폐쇄형 루프 테스트가 적합하다.

반면 ‘초당 50개의 요청이 도착할 때 p99 지연 시간은 얼마인가?’라는 질문에는 개방형 루프 테스트가 적합하다.

흔히 발생하는 오류는 폐쇄형 루프 테스트에서 ‘동시 요청 100개일 때 TTFT p99가 800ms’라는 결과를 얻은 후, 실제 운영 환경에서 초당 100개의 요청이 유입되어도 시스템이 감당할 수 있다고 판단하는 것이다.

하지만 시스템이 느려지면 폐쇄형 루프에서 동시 요청 100개를 유지하더라도 실제 초당 전송되는 요청 수는 30개에 불과할 수 있다.

요청 도착 패턴

개방형 루프 테스트에서는 요청 간격을 어떤 방식으로 설정할지도 결정해야 한다.

Constant 모드

매 1/λ초마다 요청 하나를 전송한다. 요청 간격이 항상 일정하다.

Poisson 모드

요청 간격이 지수분포를 따른다.

평균 간격은 동일하게 1/λ이지만, 요청이 짧은 시간에 집중되거나 일정 시간 동안 요청이 발생하지 않는 구간이 자연스럽게 나타난다. 이는 독립적인 사용자가 무작위로 요청을 전송하는 상황을 모사하는 수학적 모델이며, AIPerf의 기본 모드이기도 하다. Constant 모드는 요청 간격이 일정해 대기열이 갑자기 증가할 가능성이 상대적으로 낮다. 반면 Poisson 모드에서는 같은 1초 동안 여러 요청이 집중적으로 도착할 수 있으며, 이로 인해 지연 시간의 긴 꼬리(Long Tail)가 발생할 수 있다. 따라서 Constant 모드에서 측정한 p99는 실제 트래픽에 가까운 테스트 결과보다 낙관적으로 나타나는 경우가 많다.

3.1 부하 스캔과 Knee 전환점

하나의 부하 수준에서 얻은 결과는 ‘해당 부하에서 시스템이 어떻게 동작하는가’만 보여준다. 시스템이 감당할 수 있는 최대 부하를 확인하려면 동시 요청 수를 점진적으로 증가시켜야 한다. 예를 들어 로그 스케일을 기준으로 1, 2, 4, 8…과 같이 부하를 증가시키고 각 지점에서 전체 벤치마크를 수행한 뒤 처리량과 지연 시간 곡선을 그린다.

그림 3. 동시 요청 수 스캔에 따른 처리량 및 지연 시간 곡선. 처리량은 점차 포화되는 반면 지연시간은 급격히 증가할 수 있으며(왼쪽), SLO 임계값과의 교점을 통해 시스템 용량을 판단할 수 있다(오른쪽).

동시 요청 수가 증가하면 처리량은 일반적으로 계속 증가하지만 증가 폭은 점차 줄어든다. 어느 지점 이후에는 처리량이 정체되거나 소폭 감소할 수도 있다. 반면 지연시간은 초기에는 큰 변화가 없다가 특정 지점을 넘어서면 급격하게 증가할 수 있다. 이러한 전환 구간을 Knee(전환점)라고 한다. 이상적인 운영 지점은 일반적으로 Knee에 도달하기 직전 부근에 위치한다.

그래프에 SLO 지연시간 임계값을 추가하면 지연시간 곡선과 임계값이 만나는 지점을 통해 ‘SLO를 충족하는 최대 동시 요청 수’를 판단할 수 있다.

여기에 해당 부하에서의 Goodput까지 함께 확인하면 시스템의 실제 운영 성능을 더욱 종합적으로 평가할 수 있다.

3.2 워밍업과 테스트 간 간섭 방지

워밍업(Warmup)

추론 서비스를 처음 시작한 직후 수십 초 동안의 성능은 안정적인 운영 상태와 크게 다를 수 있다. 그 원인에는 모델 가중치 로딩, CUDA 커널의 최초 컴파일, KV Cache 메모리 풀 할당, 네트워크 연결 설정 등이 포함된다. AIPerf 방법론 문서의 예시에서는 최초 10초 동안의 TTFT가 정상 상태보다 약 41% 높게 나타났다. 이 구간을 통계에 포함하면 p99가 실제 정상 운영 상태보다 나쁘게 측정될 수 있으며, 영향 정도도 벤치마크 실행 시간에 따라 달라진다. 따라서 정식 측정 전에 일정 시간 워밍업 테스트를 수행하고, 해당 요청을 워밍업 데이터로 구분해 통계에서 제외하는 것이 바람직하다.

여러 테스트의 동시 실행 방지

두 엔지니어가 동일한 엔드포인트에 각각 동시 요청 32개로 벤치마크를 실행한다고 가정해 보자. 이 경우 서버가 실제로 처리해야 하는 미완료 요청 수는 64개에 이를 수 있다. 이 요청들은 동일한 GPU, 배치 스케줄러, KV Cache 메모리 풀을 공유한다. 따라서 두 테스트 결과 모두 단독으로 동시 요청 32개를 실행했을 때보다 지연 시간이 나쁘게 나타날 수 있다. 하지만 개별 보고서만으로는 그 원인을 파악하기 어렵다. 따라서 동일한 엔드포인트에 대해서는 한 번에 하나의 벤치마크만 실행해야 한다. 테스트 시작 전 다른 벤치마크가 실행 중이지 않은지 확인하는 절차가 필요하며, 단순한 구두 안내에만 의존해서는 안 된다. 이 원칙은 부하를 발생시키는 클라이언트 장비에도 적용된다. 클라이언트의 CPU 자원이 부족하거나, 사용 가능한 포트가 고갈되거나, JSON 파싱 작업으로 이벤트 루프가 지연되면 측정 결과에 클라이언트 측 병목이 포함될 수 있다.

이 경우 측정된 지연 시간은 서버 자체의 성능만을 나타내지 않는다. 또한 테스트 결과의 비교 가능성에 영향을 미치는 세 가지 요소가 있다.

첫째, 표본 수다.

각 부하 지점에서 최소 수백 개의 요청 데이터를 확보하는 것이 바람직하다. 요청이 100개뿐이라면 p99는 사실상 가장 느린 요청 하나에 가까워져 통계적 의미가 제한적이다.

둘째, 워크로드 특성이다.

합성 데이터(Synthetic Data)는 고정된 ISL과 OSL을 사용하므로 통제 가능하고 재현 가능한 용량 측정에 적합하다. 반면 실제 대화 기록은 입력과 출력 길이가 긴 꼬리 분포를 보이는 경우가 많아 합성 데이터에서는 드러나지 않는 문제를 발견할 수 있다. 두 유형 모두 테스트할 가치가 있지만 결과를 직접 비교하는 것은 적절하지 않다.

셋째, 재현 가능성이다.

난수 시드(Random Seed)를 고정하면 프롬프트 내용과 Poisson 도착 간격을 재현할 수 있다. 그러나 지연시간 자체에는 여전히 자연스러운 변동이 존재한다. 따라서 두 가지 설정을 비교할 때는 각각 여러 차례 테스트하고 중앙값을 비교하는 것이 좋다. 모델, 추론 엔진 버전, 하드웨어, Tokenizer, 부하 모델, 워밍업 설정, SLO 임계값도 함께 기록해야 한다.

그렇지 않으면 나중에 동일한 테스트 결과를 재현하기 어렵다.

4. AIPerf: 실제 도구에서는 이러한 개념이 어떻게 구현될까?

AIPerf는 NVIDIA Dynamo 팀이 오픈소스로 공개한 LLM 추론 성능 벤치마크 도구다. Apache 2.0 라이선스를 사용하며, GenAI-Perf의 후속 도구이기도 하다. 여기서는 설치 및 실행 절차 대신 세 가지 주요 설계 요소를 통해 앞서 설명한 개념을 살펴본다.

그림 4. AIPerf의 세 가지 평면: 제어 평면은 부하 모델을 결정하고, 데이터 평면은 측정을 수행하며, 분석 평면은 통계를 계산한다.

다중 프로세스와 Credit(요청 허가)

AIPerf는 약 10개의 서비스를 각각 독립적인 프로세스로 실행하고 ZeroMQ를 통해 통신한다.

전체 구조는 세 가지 평면으로 구분된다.

– 제어 평면(Control Plane): 언제, 얼마나 많은 요청을 전송할지 결정

– 데이터 평면(Data Plane): Worker가 실제 HTTP 요청을 전송하고 타임스탬프를 기록

– 분석 평면(Analysis Plane): 지표를 계산하고 결과를 내보내는 역할

AIPerf의 핵심 부하 제어 메커니즘 중 하나는 Credit이다. Credit은 일종의 ‘요청 통행증’으로 이해할 수 있다.

Worker는 Credit을 획득해야 요청 하나를 전송할 수 있으며, Credit이 없으면 요청을 전송할 수 없다. 고정 동시 요청 모드에서는 Timing Manager가 처음에 N개의 Credit을 발급한다. Credit 하나가 반환될 때마다 새로운 Credit 하나를 발급한다. 이것이 폐쇄형 루프 방식이다. Request Rate 모드에서는 이전 Credit의 반환을 기다리지 않고 Poisson 도착 스케줄에 따라 Credit을 발급한다. 이것이 개방형 루프 방식이다. 즉, 부하 모델의 핵심은 ‘어떤 조건에서 다음 요청 통행증을 발급할 것인가’를 결정하는 데 있다.

타임스탬프와 Tokenizer

Worker는 요청 전송 시점과 각 스트리밍 Chunk가 클라이언트에 도착한 시점의 타임스탬프를 기록한다. TTFT, ITL, Request Latency는 이러한 타임스탬프의 차이를 계산해 산출한다. Benchmark Duration은 전체 테스트의 실제 경과 시간을 기준으로 계산한다. 

ISL과 OSL은 Tokenizer를 사용해 계산한다.

따라서 벤치마크를 실행할 때는 Tokenizer를 지정해야 하며, 서버에 실제로 로드된 모델과 일치해야 한다.

부하 스캔과 적응형 탐색

격자 스캔(Grid Search)은 기본적으로 로그 스케일에 따라 분포된 8개의 동시 요청 지점을 테스트한다. 1, 3, 7, 19, 52, 139, 373, 1000 각 지점에서 테스트를 여러 차례 반복해 결과의 신뢰구간을 추정할 수도 있다. ‘SLO를 충족하는 최대 동시 요청 수’를 직접 찾고 싶다면 max-concurrency-under-sla와 같은 Search Recipe를 사용할 수 있다. 기본 알고리즘은 먼저 부하를 단계적으로 증가시켜 최초로 임계값을 초과하는 지점을 찾는다. 그다음 가능한 경계 범위를 좁힌다. 이후 등위 회귀(Isotonic Regression)를 활용해 측정 노이즈를 줄이고, 보간법으로 교점을 추정한다. 마지막으로 경계 부근에서 추가 테스트를 수행해 탐색 구간의 폭이 5% 미만이 될 때까지 범위를 좁힌다. 전체 과정에는 약 13~25회의 반복 테스트가 필요하다. 반면 고정된 8개 지점만 테스트하면 용량 경계가 동시 요청 139개와 373개 사이에 있다는 정도만 판단할 수도 있다. 목표가 ‘SLO를 충족하면서 최대 처리량을 달성하는 것’이라면 AIPerf는 베이지안 최적화(Bayesian Optimization)를 활용할 수 있다. 가우시안 프로세스(Gaussian Process)를 사용해 동시 요청 수에 따른 처리량을 예측하는 모델을 구성한다. 각 반복 단계에서는 예상 성능이 높거나 불확실성이 큰 지점을 선택해 테스트한다. 여러 차례 연속으로 성능 개선이 나타나지 않으면 탐색을 종료한다. 탐색해야 할 매개변수의 차원이 증가할수록 이러한 방식은 고정 격자 스캔보다 유리할 수 있다.

결론

추론 서비스의 운영 성능을 평가하려면 먼저 질문을 측정하고 검증할 수 있는 형태로 바꿔야 한다.

예를 들어 다음과 같이 정의할 수 있다. “지정된 모델, 추론 엔진, ISL/OSL 워크로드 환경에서 폐쇄형 루프 동시 요청 스캔을 수행하고 워밍업 데이터를 제외했을 때, TTFT의 p95를 Xms 미만, ITL의 p95를 Yms 미만으로 유지할 수 있는 최대 동시 요청 수는 얼마인가? 그리고 이때의 Goodput은 얼마인가?” 최종 결과는 처리량과 지연 시간 곡선, SLO 임계값, 그리고 이들의 교점을 하나의 그래프로 나타낼 수 있다.

테스트를 시작하기 전에 다음 사항을 확인하자.

  1. 스트리밍이 활성화되어 있는가? TTFT와 ITL을 측정할 수 있는지를 결정한다.
  2. 출력 길이를 제어하고 있는가? Tokenizer는 모델과 일치하는가?
  3. 시스템 용량을 확인하려면 폐쇄형 루프 동시 요청 스캔을 사용하고, 최대 트래픽 상황에서의 지연시간을 검증하려면 개방형 루프의 고정 도착률 방식을 사용한다. 개방형 루프에서는 Poisson 도착 패턴을 적용할 수 있다.
  4. 워밍업 요청을 통계에서 제외했는가? 동일한 엔드포인트에서 다른 벤치마크가 동시에 실행되고 있지는 않은가?
  5. 평균값뿐 아니라 p95, p99와 같은 백분위 지표를 확인하고 있는가? Throughput뿐 아니라 Goodput도 함께 측정하고 있는가? 전체 테스트 설정과 요청별 기록을 보관하고 있는가?

도구는 계속 업데이트되고 GPU도 새로운 세대로 교체되겠지만, 이러한 벤치마크 점검 원칙은 앞으로도 유효할 것이다.

참고 자료

  • AIPerf 소스 코드 및 공식 문서: https://github.com/ai-dynamo/aiperf Architecture, Metrics Reference, Comprehensive LLM Benchmarking, Timing Modes Reference, Search Recipes 등의 문서 포함
  • Yu et al., “Orca: A Distributed Serving System for Transformer-Based Generative Models”, OSDI 2022(continuous batching)
  • Zhong et al., “DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving”, OSDI 2024(goodput)