vLLM 서버를 띄우는 것과 빠르게 운영하는 것은 다른 문제입니다. 같은 모델과 같은 GPU를 사용해도 요청 길이, 동시 사용자 수, 배치 설정, KV Cache 여유에 따라 체감 속도는 크게 달라집니다. 파라미터를 무작정 키우면 처리량은 늘어도 첫 토큰이 늦어질 수 있고, 반대로 지연시간만 줄이다 보면 GPU가 충분히 일하지 못할 수 있습니다.
결론부터 말하면 vLLM 튜닝은 목표 지표를 정하고, 실제 요청 형태로 기준 성능을 측정한 뒤, 한 번에 하나의 변수만 바꾸는 작업입니다. 이 글에서는 TTFT, ITL, 처리량을 기준으로 병목을 찾고 조정하는 순서를 정리합니다.
핵심 요약
- 채팅 서비스는 평균값보다 TTFT·ITL의 p95 또는 p99를 먼저 봅니다.
- 배치 크기와 동시 시퀀스를 크게 하면 처리량이 좋아질 수 있지만 KV Cache와 지연시간 부담도 커집니다.
- 긴 프롬프트가 많다면 Chunked Prefill의 토큰 예산이 중요한 조정 지점입니다.
- 공통 시스템 프롬프트가 반복된다면 Prefix Caching 효과가 큽니다.
- 버전, 모델, 입력·출력 길이, 요청률을 고정하지 않은 벤치마크는 비교 자료로 쓰기 어렵습니다.
메모리 부족이 먼저 발생한다면 vLLM CUDA OOM 해결 가이드를 먼저 확인하는 것이 좋습니다. 로컬 LLM 서버의 전체 구성은 로컬 LLM 구축 1편과 로컬 LLM 서버 구축 가이드를 함께 보면 흐름을 잡기 쉽습니다.
1. “느리다”를 세 가지 지표로 나누기
사용자가 말하는 느림은 하나가 아닙니다. 질문을 보낸 뒤 첫 글자가 늦게 나오는 문제와, 답변이 시작된 뒤 한 글자씩 천천히 생성되는 문제는 원인이 다릅니다.
지표 의미 사용자가 느끼는 현상 주요 점검 대상
| TTFT | 요청부터 첫 토큰까지 걸린 시간 | 답변 시작이 늦다 | 입력 길이, 대기열, Prefill, 배치 정책 |
| ITL·TPOT | 출력 토큰 사이의 간격 | 답변이 끊기거나 천천히 나온다 | Decode 부하, 동시 요청, GPU 연산 여유 |
| Throughput | 초당 처리 요청 또는 토큰 수 | 사용자가 늘면 전체가 밀린다 | 배치 크기, 동시성, KV Cache, GPU 활용률 |
| E2E latency | 요청부터 응답 완료까지 전체 시간 | 짧은 답변도 완료가 늦다 | TTFT, 생성 길이, 네트워크, 후처리 |
운영에서는 평균만 보면 안 됩니다. 평균이 1초여도 일부 요청이 10초 이상 기다린다면 사용자는 서버를 불안정하다고 느낍니다. 최소한 p50과 p95를 함께 기록하고, SLA가 중요하면 p99도 확인합니다.
2. 튜닝 전에 기준 성능부터 고정하기
성능 비교에서 가장 흔한 실수는 매번 다른 조건으로 테스트하는 것입니다. 아래 항목을 먼저 기록합니다.
- vLLM 버전과 실행 엔진 설정
- 모델 이름, dtype, 양자화 방식
- GPU 모델, GPU 수, 드라이버·CUDA 환경
- 입력 토큰 길이와 출력 토큰 길이의 분포
- 초당 요청 수와 최대 동시 요청 수
- TTFT, ITL·TPOT, E2E latency, 출력 tokens/s
운영 트래픽이 “입력 1,000토큰, 출력 200토큰, 동시 요청 20개”에 가깝다면 벤치마크도 같은 형태로 만들어야 합니다. 짧은 프롬프트 하나만 보내 얻은 숫자는 실제 서비스 용량 계획에 거의 도움이 되지 않습니다.
vllm bench serve \
--backend openai-chat \
--base-url http://127.0.0.1:8000 \
--endpoint /v1/chat/completions \
--model YOUR_MODEL_NAME \
--dataset-name random \
--random-input-len 1024 \
--random-output-len 256 \
--num-prompts 300 \
--request-rate 8 \
--max-concurrency 32 \
--percentile-metrics ttft,tpot,itl,e2el \
--metric-percentiles 50,95,99 \
--save-result \
--result-dir ./bench-results
위 숫자는 예시입니다. 서비스 로그에서 입력·출력 길이와 동시성을 먼저 구한 뒤 실제 분포에 맞게 바꾸는 것이 핵심입니다.
3. 첫 번째 조정: max_num_batched_tokens
max_num_batched_tokens는 한 스케줄링 반복에서 처리할 수 있는 토큰 예산입니다. 이 값을 키우면 더 많은 Prefill 토큰을 함께 처리할 수 있어 처리량이나 TTFT가 좋아질 가능성이 있습니다. 하지만 큰 Prefill이 Decode 작업과 경쟁하면 ITL이 나빠질 수 있고, 메모리 압력도 커집니다.
vLLM V1에서는 가능한 경우 Chunked Prefill이 기본으로 사용됩니다. 긴 Prefill을 작은 조각으로 나누고 Decode 요청과 섞어 처리해 GPU 활용률과 지연시간의 균형을 잡는 방식입니다.
조정 방향
- 대화가 끊겨 보이고 ITL이 나쁘다: 토큰 예산을 낮춘 조합을 비교합니다.
- GPU 활용률이 낮고 처리량이 부족하다: 토큰 예산을 단계적으로 높여 봅니다.
- 긴 입력의 TTFT가 문제다: 입력 길이별 p95를 분리해 비교합니다.
공식 문서도 작은 값은 ITL에, 큰 값은 TTFT와 처리량에 유리할 수 있다고 설명합니다. 다만 최적값은 모델 크기, GPU, 프롬프트 길이에 따라 달라지므로 특정 숫자를 정답처럼 복사하면 안 됩니다.
4. 두 번째 조정: max_num_seqs
max_num_seqs는 한 번에 처리할 수 있는 최대 시퀀스 수입니다. 값을 올리면 동시 요청을 더 많이 배치할 수 있지만, 각 요청이 사용할 KV Cache와 스케줄링 부담도 늘어납니다.
상황 조정 방향 확인할 부작용
| 동시 요청이 적고 GPU가 빈다 | 단계적으로 증가 | p95 TTFT·ITL 증가 여부 |
| 요청이 몰리면 선점·대기가 늘어난다 | 감소 조합 비교 | 총 처리량 하락 여부 |
| KV Cache 부족 또는 OOM이 발생한다 | 우선 감소 | 목표 동시성 충족 여부 |
max_num_batched_tokens와 max_num_seqs를 동시에 바꾸지 않는 것이 중요합니다. 한쪽을 고정하고 다른 한쪽만 바꿔야 어떤 설정이 결과를 만들었는지 알 수 있습니다.
5. GPU 메모리와 KV Cache 여유 확인하기
vLLM의 동시 처리 능력은 KV Cache 여유와 밀접합니다. gpu_memory_utilization은 모델 실행기가 사용할 GPU 메모리 비율을 정합니다. 값을 높이면 KV Cache 공간을 더 확보할 수 있지만, 같은 GPU에서 다른 프로세스가 돌거나 메모리 변동이 크면 OOM 위험이 커집니다.
vllm serve YOUR_MODEL_NAME \
--gpu-memory-utilization 0.90 \
--max-model-len 8192 \
--max-num-seqs 64 \
--max-num-batched-tokens 8192
이 명령의 숫자는 시작점 예시일 뿐 권장 정답이 아닙니다. 먼저 nvidia-smi와 vLLM 로그로 모델 가중치, KV Cache, 다른 프로세스의 사용량을 확인합니다. 실제 서비스가 8K를 넘는 문맥을 사용하지 않는다면 필요 이상으로 큰 max-model-len을 유지할 이유도 다시 검토합니다.
메모리가 부족할 때의 기본 순서는 다음과 같습니다.
- 사용하지 않는 GPU 프로세스를 정리합니다.
- 실제 요구보다 큰 최대 문맥 길이를 줄입니다.
- max_num_seqs와 max_num_batched_tokens를 낮춰 비교합니다.
- 지원되는 양자화 또는 KV Cache dtype을 검토합니다.
- 모델이 한 GPU에 적합하지 않다면 Tensor Parallel 구성을 검토합니다.
6. 반복 프롬프트에는 Prefix Caching
Automatic Prefix Caching은 이전 요청과 동일한 프롬프트 앞부분의 KV Cache를 재사용합니다. 긴 시스템 프롬프트, 동일한 문서 문맥, 공통 few-shot 예시가 반복되는 서비스에서 특히 효과가 있습니다.
vllm serve YOUR_MODEL_NAME \
--enable-prefix-caching
다만 모든 요청의 앞부분이 다르면 캐시 적중률이 낮아 효과도 작습니다. 기능을 켰다는 사실보다 실제 요청에서 공통 prefix가 얼마나 반복되는지가 중요합니다. 사용자별 정보처럼 달라지는 내용을 프롬프트 맨 앞에 배치하면 공통 prefix가 짧아질 수 있으므로 프롬프트 구조도 함께 점검합니다.
7. 멀티 GPU는 목적에 맞게 선택하기
GPU 수를 늘린다고 한 요청의 속도가 항상 같은 비율로 빨라지는 것은 아닙니다. GPU 사이 통신 비용도 생기기 때문입니다.
- Tensor Parallel: 모델이 한 GPU에 들어가지 않거나, 한 모델의 연산을 여러 GPU로 나눠야 할 때 검토합니다.
- 복수 인스턴스·Replica: 모델이 한 GPU에 들어가고 독립 요청이 많다면 전체 처리량 확장에 유리할 수 있습니다.
- Pipeline Parallel: 노드나 GPU 구성상 모델 레이어를 단계로 나눠야 할 때 검토합니다.
단일 요청 latency가 목표인지, 전체 QPS가 목표인지 먼저 정하지 않으면 GPU를 늘리고도 기대한 효과를 얻지 못할 수 있습니다.
8. 운영에서 반드시 볼 관측 지표
튜닝은 일회성 벤치마크로 끝나지 않습니다. 입력 길이와 사용자 수가 바뀌면 최적점도 달라집니다.
- TTFT, ITL·TPOT, E2E latency의 p50·p95·p99
- 입력·출력 tokens/s
- Running·Waiting 요청 수
- KV Cache 사용률과 선점 발생
- GPU 사용률·메모리·전력
- 에러율, OOM 횟수, timeout 횟수
vLLM 서버의 /metrics를 Prometheus로 수집하면 서버 수준의 지표를 볼 수 있습니다. 요청별 분석이 필요하면 지원 버전에서 --enable-per-request-metrics도 검토할 수 있지만, 공식 문서는 높은 동시성에서 CPU 오버헤드가 생길 수 있다고 안내하므로 운영 적용 전에 부하 테스트가 필요합니다.
9. 증상별 튜닝 순서
증상 먼저 확인 다음 실험
| 첫 토큰이 늦다 | 입력 길이, 대기열, TTFT p95 | 토큰 예산, 동시성, Prefix Caching |
| 생성 중 끊긴다 | ITL p95, 긴 Prefill 유입 | 작은 토큰 예산, 요청 동시성 제한 |
| GPU가 충분히 일하지 않는다 | GPU 사용률, 대기 요청, 배치 크기 | 토큰 예산·시퀀스 수 단계적 증가 |
| 요청이 몰리면 OOM | KV Cache 사용, 최대 문맥 길이 | 시퀀스·토큰 예산 감소, 메모리 설정 재검토 |
| 동일한 긴 시스템 프롬프트가 반복된다 | 공통 prefix 길이 | Prefix Caching A/B 테스트 |
10. 가장 안전한 A/B 테스트 방법
- 현재 운영 설정으로 기준 결과를 저장합니다.
- 한 번에 한 파라미터만 변경합니다.
- 동일한 요청 데이터와 동일한 요청률로 세 번 이상 반복합니다.
- 평균이 아니라 p95·p99와 에러율을 함께 비교합니다.
- 목표 지표가 좋아져도 다른 지표가 SLA를 벗어나면 채택하지 않습니다.
- 채택한 설정과 vLLM 버전을 함께 기록합니다.
예를 들어 처리량만 15% 늘고 p95 TTFT가 두 배가 됐다면 채팅 서비스에는 나쁜 변경일 수 있습니다. 반대로 야간 배치 추론이라면 TTFT보다 전체 완료 시간이 중요하므로 좋은 변경일 수 있습니다.
11. 흔한 실수
실수 1. GPU 사용률 100%만 목표로 한다
GPU를 꽉 쓰는 것은 수단이지 목적이 아닙니다. 사용자 SLA를 지키면서 필요한 처리량을 내는 것이 목표입니다.
실수 2. 짧은 프롬프트로만 벤치마크한다
RAG나 문서 요약 서비스는 입력 길이가 성능을 크게 좌우합니다. 운영 길이 분포를 반영해야 합니다.
실수 3. 여러 값을 한꺼번에 바꾼다
결과가 좋아져도 어떤 설정 때문인지 알 수 없고, 문제가 생겼을 때 되돌리기 어렵습니다.
실수 4. 버전 차이를 무시한다
vLLM은 엔진과 기본값이 빠르게 바뀝니다. 인터넷의 오래된 튜닝 값을 그대로 적용하지 말고 현재 버전의 공식 문서를 확인해야 합니다.
12. 최종 결론
vLLM 속도 튜닝의 핵심은 가장 큰 숫자를 찾는 것이 아니라 서비스 목표에 맞는 균형점을 찾는 것입니다.
- TTFT, ITL, 처리량 중 우선 목표를 정합니다.
- 실제 입력·출력 길이와 동시성으로 기준 성능을 측정합니다.
- max_num_batched_tokens와 max_num_seqs를 한 번에 하나씩 조정합니다.
- KV Cache와 GPU 메모리 여유를 함께 봅니다.
- 공통 프롬프트가 반복된다면 Prefix Caching을 비교합니다.
- 최종 결정은 p95·p99와 에러율까지 포함해 내립니다.
FAQ
Q1. 처리량을 높이려면 어떤 값을 가장 먼저 올려야 하나요?
일반적으로 max_num_batched_tokens와 max_num_seqs가 주요 후보지만, 먼저 GPU 활용률과 대기 요청이 있는지 확인해야 합니다. 병목이 네트워크나 전처리에 있다면 두 값을 올려도 효과가 없습니다.
Q2. Prefix Caching은 항상 켜는 것이 좋은가요?
공통 prefix가 반복되는 서비스에서는 유리하지만, 요청 앞부분이 대부분 다르면 효과가 제한적입니다. 캐시 적중 가능성이 높은 실제 요청으로 A/B 테스트하는 편이 안전합니다.
Q3. gpu_memory_utilization을 높이면 무조건 빨라지나요?
KV Cache 공간이 늘어 동시 처리에 도움이 될 수 있지만, 다른 GPU 프로세스와 충돌하거나 메모리 변동으로 OOM이 날 수 있습니다. 여유 메모리와 에러율을 함께 확인해야 합니다.
Q4. Tensor Parallel을 쓰면 속도가 두 배가 되나요?
그렇지 않습니다. 모델 연산을 나눌 수 있지만 GPU 간 통신 비용이 생깁니다. 모델 적재, 단일 요청 latency, 전체 처리량 중 무엇이 목표인지에 따라 효과가 달라집니다.
참고 공식 문서
- vLLM Optimization and Tuning
- vLLM Bench Serve
- vLLM Automatic Prefix Caching
- vLLM Per-Request Metrics
함께 읽으면 좋은 글
'AI·LLM 엔지니어링' 카테고리의 다른 글
| vLLM KV Cache 완전정리 — 메모리 계산·Prefix Caching·FP8 튜닝법 (2) | 2026.07.26 |
|---|---|
| LLM 양자화 완전정리 — FP16·INT8·INT4, GGUF·AWQ·GPTQ 선택법 (0) | 2026.07.26 |
| vLLM CUDA OOM 해결 가이드 (2026): GPU 메모리 부족 원인별 튜닝 순서 (0) | 2026.07.18 |
| vLLM vs Ollama 완벽 비교 - 로컬 LLM 서빙, 무엇을 선택해야 할까? (2026) (0) | 2026.07.16 |
| Claude Code, Cursor, Codex 실제 개발 생산성 비교: 어떤 AI 코딩 도구가 맞을까? (0) | 2026.07.15 |