요청별 메트릭

요청별 메트릭 (Per-Request Metrics)

서버가 전체적으로 얼마나 빠른지 보는 것도 중요하지만, "이 개별 요청 하나는 얼마나 걸렸지?"가 더 궁금할 때가 있어요. 특히 과금(billing), SLA 모니터링, 지연 분석을 하려면 요청 단위의 상세한 타이밍이 필요하죠. vLLM의 요청별 메트릭(Per-Request Metrics) 은 개별 요청의 타이밍 메트릭을 API 응답에 직접 담아 돌려주는 기능이에요. /metrics에서 제공하는 서버 집계형 Prometheus 메트릭을 보완하는 역할을 합니다.

출처: vLLM 공식 문서 — per_request_metrics

활성화하기 (Enabling)

서버를 --enable-per-request-metrics 플래그로 시작하면 돼요.

vllm serve meta-llama/Llama-3.1-8B-Instruct --enable-per-request-metrics

이 플래그가 설정되면, 지원되는 API 응답에 각 요청에 대한 메트릭이 포함됩니다.

📌 참고: 높은 동시성(concurrency)에서는 요청별 메트릭 계산이 무시할 수 없는 CPU 오버헤드를 만들 수 있어요. 운영 환경에서 켜기 전에 자신의 워크로드로 벤치마크해서 영향을 평가해 보세요.

응답 형식 (Response Format)

요청별 메트릭이 켜져 있으면 응답에 metrics 객체가 포함됩니다.

{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "model": "meta-llama/Llama-3.1-8B-Instruct",
  "choices": [ ... ],
  "usage": {
    "prompt_tokens": 42,
    "completion_tokens": 128,
    "total_tokens": 170
  },
  "metrics": {
    "time_to_first_token_ms": 85.2,
    "generation_time_ms": 1240.5,
    "queue_time_ms": 12.3,
    "mean_itl_ms": 9.1,
    "tokens_per_second": 103.2
  }
}

각 필드의 의미를 정리하면 이렇습니다.

필드 설명
time_to_first_token_ms 요청이 스케줄링된 시점부터 첫 출력 토큰이 생성될 때까지의 시간 (TTFT).
generation_time_ms 디코드 시간: 첫 출력 토큰부터 마지막 출력 토큰까지. 대기 시간(queue wait)과 프리필/TTFT는 제외.
queue_time_ms 처리가 시작되기 전에 요청이 스케줄러 큐에서 대기한 시간.
mean_itl_ms 디코드 단계에서 연속 출력 토큰 사이의 평균 시간(mean inter-token latency). 단일 토큰 응답에서는 null.
tokens_per_second 전체 출력 토큰 처리량: 추론 구간(스케줄링부터 마지막 출력 토큰까지) 동안 생성된 모든 토큰. generation_time_ms와 달리 프리필 단계를 포함하므로, 순수 디코드 속도가 아닌 end-to-end 생성 속도를 반영.

해당 요청의 기반 타이밍 데이터를 구할 수 없으면 모든 필드가 null이 됩니다.

📌 참고: 타이밍 메트릭은 하나의 생성 스트림을 묘사하기 때문에, 요청이 정확히 하나의 스트림으로 매핑될 때만 반환돼요. n > 1인 요청에서는 metrics 객체가 null로 억제됩니다. 그 이유는 기반 타이밍 데이터가 n개의 시퀀스 중 하나만 반영해서 요청 전체에 정확히 귀속시킬 수 없기 때문이죠. 다만 토큰 사용량(prompt_tokens, completion_tokens)은 이런 경우에도 정확하게 유지돼요. 또 요청별 메트릭은 서버 측 통계 로깅이 필요하며, 이는 기본적으로 켜져 있어요. --disable-log-stats도 함께 설정하면 vLLM은 --enable-per-request-metrics를 거부합니다.

예시 요청 (Example Request)

비스트리밍 (Non-streaming)

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="token")

response = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[{"role": "user", "content": "What is the capital of France?"}],
)

print(response.usage)
print(response.model_extra.get("metrics"))

스트리밍 (Streaming)

스트리밍 응답에서는 메트릭이 마지막 usage 청크(모든 콘텐츠 청크 이후에 전송되는 청크)에 붙어요. 그 청크는 stream_options.include_usage: true로 usage 보고를 켜거나, 서버에서 --enable-force-include-usage로 강제했을 때만 발행됩니다. 강제로 켜지 않은 경우 스트리밍 클라이언트는 메트릭을 받으려면 stream_options.include_usage: true를 설정해야 해요.

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="token")

stream = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[{"role": "user", "content": "What is the capital of France?"}],
    stream=True,
    stream_options={"include_usage": True},
)

for chunk in stream:
    if chunk.usage:
        print("Usage:", chunk.usage)
        print("Metrics:", chunk.model_extra.get("metrics"))

Completions API

요청별 메트릭은 /v1/completions 엔드포인트에서도 같은 metrics 응답 필드로 사용할 수 있어요. n > 1일 때와 마찬가지로, 여러 개의 프롬프트를 가진 요청에서는 타이밍 데이터를 단일 프롬프트 생성에 귀속시킬 수 없기 때문에 메트릭이 생략됩니다.

Prometheus 메트릭과의 관계 (Relationship to Prometheus Metrics)

정리하자면 둘은 역할이 달라요.

  • metrics 응답 필드는 단일 요청에 대한 요청별 값입니다.
  • /metrics Prometheus 엔드포인트는 서버 수준의 히스토그램(예: vllm:time_to_first_token_seconds)을 노출하며, 모든 요청을 집계해요.

즉, "하나의 요청을 깊게" 보려면 응답 필드를, "전체를 한눈에" 보려면 Prometheus를 쓰면 됩니다.

추측 디코딩 수용 메트릭 (Speculative Decoding Acceptance)

추측 디코딩이 켜져 있을 때, 요청별 수용(acceptance) 메트릭(평균 수용 길이, 수용된 드래프트 길이 분포)은 --per-request-spec-decode-metrics로 반환할 수 있어요. 이들은 같은 metrics 객체 안에 metrics.speculative_decoding으로 포함되며, 타이밍 필드와 마찬가지로 단일 시퀀스(n == 1) 요청에서만 보고됩니다.

더 알아보기 (Learn more)