목차
- 로컬 LLM 성능은 GPU 이름보다 측정 방식에서 갈립니다
- vLLM 벤치마크 전에 성능 지표를 운영 관점으로 잡기
- LLM 벤치마크 환경은 README처럼 고정하세요
- vLLM 서버는 옵션 파일로 남기세요
- 추론 성능 측정은 워밍업, 단일 요청, 동시성 순서로 갑니다
- vLLM 내장 벤치마크로 AI 성능 비교 기준선 만들기
- 로컬 LLM 성능 테스트에서 자주 보이는 실패 모드
- 검증과 결과 해석: 평균값보다 꺾이는 지점을 찾으세요
- 설정값 선택 기준: 빠르게보다 맞게 고르는 게 먼저입니다
- 자주 묻는 질문
- Q. vLLM만 쓰면 무조건 빨라지나요?
- Q. 로컬 LLM 성능 비교에서 가장 먼저 볼 지표는 뭔가요?
- Q. 블로그에 있는 벤치마크 숫자를 믿어도 되나요?
- Q. 양자화 모델은 항상 좋은 선택인가요?
- Q. GPU 사용률이 낮으면 vLLM 설정 문제인가요?
- 마지막 판단: 운영선은 가장 빠른 지점이 아니라 덜 흔들리는 지점입니다
로컬 LLM 성능: vLLM 벤치마크 측정 방법론
로컬 LLM 성능은 GPU 이름보다 측정 방식에서 갈립니다
로컬 LLM 성능을 비교할 때 가장 위험한 문장은 ‘이 GPU면 충분히 빠르겠지’입니다. 실제 운영에 가까워질수록 병목은 GPU 코어 하나로 설명되지 않거든요. 프롬프트를 읽는 prefill, 토큰을 하나씩 생성하는 decode, KV cache가 차지하는 메모리, tokenizer와 HTTP 클라이언트, 동시 요청 스케줄링이 같이 움직입니다.
저는 벤치마크를 볼 때 숫자 하나만 믿지 않습니다. 초당 토큰 수가 좋아도 첫 토큰이 늦으면 채팅 UX는 답답합니다. 반대로 TTFT가 괜찮아도 동시 요청을 올렸을 때 p95 지연 시간이 크게 튀면 내부 서비스로 쓰기 불안하더라고요.
이 글은 vLLM을 띄우는 법을 소개하는 데서 멈추지 않습니다. 어떤 값을 고정하고, 무엇을 바꿔야 비교가 성립하는지, vLLM 옵션이 성능·비용·안정성에 어떤 영향을 주는지, 결과를 보고 운영선을 어떻게 잡는지까지 정리합니다. 특정 GPU의 성능 수치는 지어내지 않겠습니다. 대신 여러분 장비에서 재현할 수 있는 로컬 LLM 성능 측정 절차와 판단 기준을 남기겠습니다.

로컬 GPU 서버에서 vLLM API 서버를 띄우고, 벤치마크 클라이언트가 요청을 보내며, GPU/시스템 지표를 함께 수집하는 흐름입니다.
vLLM 벤치마크 전에 성능 지표를 운영 관점으로 잡기
vLLM은 OpenAI 호환 API 서버로 사용할 수 있는 LLM 추론 엔진입니다. 핵심은 여러 요청을 효율적으로 스케줄링하고, KV cache를 관리하며, 서버형 추론에서 처리량을 끌어올리는 데 있습니다. 여기서 성능 지표는 사전식 정의보다 ‘어떤 장애를 미리 알려주는가’로 보는 편이 훨씬 쓸모 있습니다.
- TTFT(Time To First Token): 사용자가 요청한 뒤 첫 토큰이 나오기까지의 시간입니다. 길면 모델이 똑똑해도 느리게 느껴집니다. 긴 입력, prefill 부하, 큐 대기 시간이 주로 영향을 줍니다.
- TPOT(Time Per Output Token): 첫 토큰 이후 출력 토큰 하나를 생성하는 평균 시간입니다. 답변이 길어질수록 체감 속도를 좌우합니다.
- ITL(Inter Token Latency): 토큰 사이 간격입니다. 스트리밍 응답이 뚝뚝 끊기는지 확인할 때 봅니다.
- E2E Latency: 요청부터 최종 응답 완료까지의 전체 시간입니다. 배치 요약, 문서 처리, 자동화 워크플로에서는 이 값이 더 중요할 수 있습니다.
- Throughput: 초당 처리한 요청 수 또는 토큰 수입니다. 사용자 수를 늘릴 때 비용 효율을 판단하는 기준입니다.
- Concurrency: 동시에 처리 중인 요청 수입니다. vLLM의 장점은 보통 단일 요청보다 이 구간에서 더 잘 드러납니다.
- GPU Memory: 모델 가중치, KV cache, CUDA graph, 런타임 오버헤드가 함께 차지하는 메모리입니다. OOM은 대개 모델 크기 하나가 아니라 컨텍스트 길이와 동시성이 합쳐져 발생합니다.
현장에서 가장 많이 본 착시는 ‘초당 토큰이 높으니 빠르다’는 판단입니다. 문서 요약처럼 입력이 긴 워크로드는 prefill 비용이 커서 TTFT가 먼저 무너질 수 있습니다. 반대로 짧은 채팅을 많이 받는 서비스는 decode보다 큐 대기와 스케줄링이 더 큰 문제가 되기도 합니다. 그래서 LLM 벤치마크는 항상 워크로드를 먼저 정하고 들어가야 합니다.
LLM 벤치마크 환경은 README처럼 고정하세요
LLM 벤치마크에서 비교가 깨지는 가장 흔한 이유는 실험 조건이 매번 조금씩 달라지는 겁니다. 모델만 같아도 토크나이저 버전, dtype, max model length, 요청 분포, temperature, 스트리밍 여부가 바뀌면 숫자는 달라집니다. 실험을 시작하기 전에 아래 항목을 먼저 적어두세요. 이거 진짜 편합니다. 나중에 결과가 이상할 때 기록이 없으면 원인 추적이 거의 감으로 변하거든요.
| 항목 | 기록할 내용 | 왜 중요한가 |
|---|---|---|
| vLLM 버전 | <code>vllm –version, 설치 방식, 컨테이너 태그 | CLI 옵션과 스케줄러 동작은 버전에 따라 바뀔 수 있습니다. |
| GPU/드라이버 | nvidia-smi 출력, CUDA 런타임, GPU 개수 |
메모리 용량과 커널 지원 여부가 추론 안정성에 직접 영향을 줍니다. |
| 모델 | Hugging Face ID, 로컬 경로, revision, 양자화 여부 | 가중치와 토크나이저가 다르면 같은 이름처럼 보여도 비교가 아닙니다. |
| 입력 길이 | 평균/최대 prompt token, 테스트 데이터셋 | 긴 입력은 prefill 비용과 KV cache 사용량을 키웁니다. |
| 출력 길이 | max_tokens, 고정 출력 길이 사용 여부 |
decode 성능을 비교하려면 출력 길이를 통제해야 합니다. |
| 동시성 | 1, 2, 4, 8, 16처럼 단계별 증가 | 처리량이 늘다가 지연만 커지는 지점을 찾아야 합니다. |
| 서버 옵션 | dtype, max-model-len, gpu-memory-utilization, max-num-seqs |
메모리, 큐잉, 처리량, OOM 위험이 여기서 크게 갈립니다. |
| 요청 방식 | chat/completions 여부, streaming 여부, timeout | 클라이언트 측 지연과 서버 처리 지연을 분리해 봐야 합니다. |
‘테스트 데이터셋’이라는 말에 너무 겁먹을 필요는 없습니다. 사내 문서 요약용이면 실제 문서에서 민감정보를 제거한 샘플 30~100개, 코딩 보조용이면 짧은 질문·중간 길이 수정 요청·긴 파일 기반 요청을 나눠 준비하면 됩니다. 핵심은 매번 같은 입력 분포를 쓰는 것입니다.
vLLM 서버는 옵션 파일로 남기세요
설치는 환경 의존성이 큽니다. CUDA, PyTorch, 드라이버 조합은 공식 설치 문서를 확인하는 편이 안전합니다. 아래 명령은 NVIDIA GPU가 있는 Linux 환경에서 시작점을 잡는 예시로 보세요. 벤치마크 재현성을 위해 서버 실행 옵션은 셸 히스토리에만 남기지 말고 YAML 파일로 고정하는 걸 권합니다.
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install vllm
vllm --version
python - <<'PY'
import torch
print('cuda_available=', torch.cuda.is_available())
print('gpu_count=', torch.cuda.device_count())
PY
아래는 단일 GPU에서 시작할 때의 보수적인 예시입니다. 모델 경로는 여러분 환경에 맞게 바꾸세요. vLLM의 --config는 YAML 설정 파일을 읽을 수 있으며, 같은 값이 CLI와 설정 파일에 동시에 있으면 CLI 인자가 우선합니다. 그래서 실제 운영에서는 중복을 줄이고, 실험에서는 어떤 값이 적용됐는지 로그로 한 번 더 확인하는 습관이 좋습니다.
# vllm-serve.yaml
model: /models/your-llm-model
host: 0.0.0.0
port: 8000
dtype: auto
max_model_len: 4096
gpu_memory_utilization: 0.85
max_num_seqs: 16
enable_prefix_caching: true
disable_log_requests: true
vllm serve --config vllm-serve.yaml
max_model_len은 서버가 받아들일 입력+출력의 최대 토큰 길이입니다. 무작정 크게 잡으면 긴 문서를 받을 수는 있지만 KV cache 여유가 줄어 동시성이 줄고 OOM 가능성이 커집니다. gpu_memory_utilization은 vLLM이 모델 실행에 사용할 GPU 메모리 비율입니다. 높이면 더 큰 KV cache를 확보할 수 있지만, 드라이버·라이브러리·다른 프로세스가 쓸 여유 공간은 줄어듭니다.
max_num_seqs는 한 번의 스케줄링 반복에서 처리할 수 있는 시퀀스 수의 상한으로 이해하면 쉽습니다. 짧은 채팅을 많이 받는 서버라면 늘려볼 가치가 있고, 긴 문서 요약처럼 요청 하나가 큰 경우에는 과하게 늘릴수록 지연 시간과 메모리 압박이 커질 수 있습니다. enable_prefix_caching은 같은 시스템 프롬프트나 반복 prefix가 많은 워크로드에서 효과를 기대할 수 있지만, 요청 prefix가 매번 완전히 다르면 체감이 작을 수 있습니다.

vLLM 서버 실행 옵션과 벤치마크 클라이언트가 연결되는 구조를 한눈에 볼 수 있는 구성도입니다.
추론 성능 측정은 워밍업, 단일 요청, 동시성 순서로 갑니다
서버가 떠도 바로 측정값을 믿으면 안 됩니다. 첫 요청에는 모델 로딩 이후 남은 초기화, CUDA kernel 준비, 캐시 관련 비용이 섞일 수 있습니다. 저는 보통 헬스 체크, 워밍업, 단일 요청 확인, 동시성 테스트 순서로 갑니다. 이 순서를 지키면 서버가 느린 건지, 첫 요청만 느린 건지, 클라이언트가 병목인지 분리하기 쉽습니다.
curl -s http://127.0.0.1:8000/v1/models | jq .
for i in 1 2 3; do
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "/models/your-llm-model",
"messages": [{"role": "user", "content": "warmup"}],
"max_tokens": 16,
"temperature": 0
}' > /dev/null
echo warmup-$i-done
done
간단한 smoke test에서는 응답이 오는지만 보지 말고 usage 필드도 확인하세요. 프롬프트 토큰과 completion 토큰이 예상보다 크게 다르면 테스트 입력이 통제되지 않은 것입니다. 특히 한국어와 영어는 토크나이저에서 토큰 수가 다르게 나올 수 있으니, 같은 글자 수가 곧 같은 부하라는 착각을 피해야 합니다.
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "/models/your-llm-model",
"messages": [
{"role": "system", "content": "간결하게 답하세요."},
{"role": "user", "content": "로컬 LLM 성능 측정에서 TTFT가 왜 중요한지 설명해줘."}
],
"max_tokens": 128,
"temperature": 0
}' | jq '{id, usage, finish_reason: .choices[0].finish_reason}'
직접 만든 부하 스크립트도 유용합니다. 아래 코드는 스트리밍 응답에서 첫 chunk가 도착하는 시점과 전체 완료 시간을 분리해 기록합니다. 전문 도구를 대체하진 않지만, TTFT와 E2E가 왜 다르게 움직이는지 눈으로 확인하기 좋습니다.
import asyncio
import json
import time
import httpx
URL = 'http://127.0.0.1:8000/v1/chat/completions'
MODEL = '/models/your-llm-model'
payload = {
'model': MODEL,
'messages': [
{'role': 'user', 'content': 'vLLM으로 로컬 LLM 성능을 측정하는 기준을 설명해줘.'}
],
'max_tokens': 128,
'temperature': 0,
'stream': True,
}
async def one_request(client, idx):
start = time.perf_counter()
first_chunk_at = None
chunks = 0
async with client.stream('POST', URL, json=payload, timeout=120) as response:
response.raise_for_status()
async for line in response.aiter_lines():
if not line.startswith('data: '):
continue
data = line.removeprefix('data: ').strip()
if data == '[DONE]':
break
chunks += 1
if first_chunk_at is None:
first_chunk_at = time.perf_counter()
end = time.perf_counter()
ttft = None if first_chunk_at is None else first_chunk_at - start
return {'request': idx, 'ttft_sec': ttft, 'e2e_sec': end - start, 'chunks': chunks}
async def run(concurrency):
limits = httpx.Limits(max_connections=concurrency, max_keepalive_connections=concurrency)
async with httpx.AsyncClient(limits=limits) as client:
results = await asyncio.gather(*(one_request(client, i) for i in range(concurrency)))
for item in results:
print(json.dumps(item, ensure_ascii=False))
if __name__ == '__main__':
asyncio.run(run(concurrency=4))
이 스크립트를 concurrency=1, 2, 4, 8로 바꿔가며 돌려보면 패턴이 보입니다. TTFT만 급격히 늘면 큐 대기나 prefill 경쟁을 의심하고, TTFT는 괜찮은데 E2E가 길어지면 decode 구간의 경쟁이나 출력 길이 분포를 봅니다. 에러가 섞이면 평균값은 잠깐 내려놓고 실패율부터 봐야 합니다.
vLLM 내장 벤치마크로 AI 성능 비교 기준선 만들기
직접 만든 스크립트는 원인을 좁히는 데 좋고, 반복 가능한 비교에는 vLLM의 벤치마크 명령이 편합니다. 버전에 따라 세부 옵션은 달라질 수 있으니 vllm bench serve --help를 먼저 확인하세요. 핵심은 backend, endpoint, model, dataset 또는 synthetic 입력 조건, 동시성, 프롬프트 수를 명시하는 것입니다.
vllm bench serve --help
vllm bench serve \
--backend openai-chat \
--base-url http://127.0.0.1:8000 \
--endpoint /v1/chat/completions \
--model /models/your-llm-model \
--num-prompts 100 \
--max-concurrency 4 \
--percentile-metrics ttft,tpot,itl,e2el
여기서 주의할 점은 ‘벤치마크 클라이언트에서 측정한 지연 시간’이라는 사실입니다. 서버 내부 처리 시간, 네트워크, 클라이언트 이벤트 루프, JSON 파싱이 한 덩어리로 섞일 수 있습니다. 그래서 같은 서버에서 한 번, 다른 머신에서 한 번, 가능하면 클라이언트 CPU 사용률까지 같이 보는 편이 좋습니다.
데이터셋 기반 테스트를 할 때는 입력 길이 분포를 꼭 기록하세요. 평균 토큰만 보면 안 됩니다. p95 입력 길이가 긴 데이터셋은 짧은 요청 다수보다 훨씬 거칠게 서버를 흔듭니다. 특히 문서 요약형 워크로드는 평균보다 꼬리가 문제입니다. 운영 장애는 보통 평균 요청이 아니라 긴 요청 몇 개가 큐를 막을 때 시작됩니다.
로컬 LLM 성능 테스트에서 자주 보이는 실패 모드
OOM과 느린 첫 응답은 표면 증상입니다. 같은 OOM이라도 원인은 모델이 너무 큰 경우, 컨텍스트를 과하게 연 경우, 동시성을 너무 높인 경우, logprobs 같은 옵션이 메모리를 더 쓰는 경우로 나뉩니다. 해결책도 다릅니다. 무조건 작은 모델로 내리면 문제는 사라지지만, 품질과 비용의 균형점을 찾을 기회를 잃습니다.
watch -n 1 nvidia-smi
nvidia-smi --query-gpu=timestamp,name,index,utilization.gpu,utilization.memory,memory.used,memory.total,power.draw \
--format=csv -l 1
| 증상 | 가능성이 높은 근본 원인 | 먼저 볼 것 | 우선 조치 |
|---|---|---|---|
| 첫 요청만 유독 느림 | 워밍업 부족, 커널 초기화, 캐시 미형성 | 워밍업 전후 TTFT 차이 | 측정 전 워밍업 요청을 제외하고 기록합니다. |
| 동시성 증가 시 TTFT가 먼저 튐 | 큐 대기, 긴 prefill 경쟁, max_num_seqs 과다 | 입력 토큰 분포, p95 TTFT | 긴 입력과 짧은 입력을 분리 측정하고 동시성 상한을 낮춥니다. |
| 출력 중간부터 느려짐 | decode 병목, 긴 출력 요청 혼재 | max_tokens, 실제 completion token | 출력 길이를 고정한 테스트와 실제 분포 테스트를 따로 돌립니다. |
| GPU 메모리가 가득 찬 뒤 OOM | KV cache 증가, 과한 max_model_len, 동시 요청 과다 | memory.used 추이, 요청별 입력 길이 | max_model_len, max_num_seqs, 동시성을 순서대로 줄입니다. |
| GPU 사용률이 낮은데 응답이 느림 | 클라이언트 병목, tokenizer/CPU 병목, 연결 재사용 실패 | 클라이언트 CPU, HTTP keep-alive, 서버 로그 | 비동기 클라이언트, 연결 풀, 클라이언트 분리 실행을 확인합니다. |
| 평균은 좋은데 p95가 나쁨 | 긴 요청 꼬리, 큐 대기 증가 | 요청 길이 p50/p95, 실패 요청 로그 | 워크로드를 길이별로 나누고 운영 한계는 평균이 아니라 p95로 잡습니다. |
운영에서는 ‘성능을 더 뽑는 옵션’보다 ‘실패할 때 예측 가능한 옵션’이 더 가치 있을 때가 많습니다. 예를 들어 gpu_memory_utilization을 올리면 실험실 처리량은 좋아질 수 있지만, 같은 GPU에서 모니터링 에이전트나 다른 프로세스가 함께 돌면 여유 메모리가 부족해질 수 있습니다. 벤치마크가 목적이면 한계를 밀어보고, 서비스가 목적이면 일부러 여유를 남기는 선택이 맞습니다.
검증과 결과 해석: 평균값보다 꺾이는 지점을 찾으세요
추론 성능 측정에서 제가 가장 신뢰하는 그림은 동시성별 TTFT, TPOT, E2E latency, tokens/sec, 실패율을 한 표에 놓은 것입니다. 평균만 있으면 위험합니다. 최소한 p50과 p95를 같이 보세요. p50은 평소 체감, p95는 불만이 시작되는 구간에 가깝습니다.
- 단일 요청에서 TTFT가 제품 UX에 맞는가?
- 동시성을 올렸을 때 처리량이 증가하는 구간과 지연만 늘어나는 구간이 어디인가?
- 먼저 오는 한계가 GPU 메모리인지, decode 처리량인지, 클라이언트 병목인지 구분했는가?
- 실패율이 0에 가까운 구간과 단순히 평균이 좋은 구간이 같은가?
예를 들어 내부 문서 요약 챗봇을 만든다고 해보겠습니다. 사용자는 한 번에 긴 문서를 넣고 답변을 기다립니다. 이 경우 짧은 프롬프트로 초당 토큰 수만 재면 거의 도움이 안 됩니다. 2천 토큰, 8천 토큰, 16천 토큰처럼 입력 길이를 나누고, 출력 길이는 고정한 뒤 TTFT와 E2E를 봐야 합니다. 반면 짧은 고객 응대 챗봇이면 입력 길이보다 동시 요청과 p95 TTFT가 더 중요합니다.

동시성 증가에 따른 응답 시간, 처리량, GPU 메모리 사용량을 함께 보여주는 결과 대시보드 이미지입니다.
| 비교 목적 | 고정할 값 | 바꿀 값 | 판단 기준 |
|---|---|---|---|
| 모델 후보 비교 | 프롬프트, max_tokens, 동시성, vLLM 옵션 | 모델 경로 또는 revision | 품질 평가는 별도로 두고, 여기서는 지연·처리량·메모리만 비교합니다. |
| 동시성 한계 확인 | 모델, 입력 길이, 출력 길이 | max-concurrency, 클라이언트 수 |
처리량 증가가 둔화되고 p95 지연이 급등하기 직전이 운영 후보입니다. |
| 긴 컨텍스트 영향 | 모델, 출력 길이, 동시성 | 입력 토큰 길이, max_model_len |
TTFT와 GPU 메모리가 함께 증가하는지 봅니다. |
| 서버 옵션 튜닝 | 모델, 테스트 데이터, 클라이언트 | gpu_memory_utilization, max_num_seqs |
평균 처리량보다 실패율과 p95 안정성을 우선합니다. |
| prefix cache 효과 확인 | 시스템 프롬프트, 모델, 동시성 | 반복 prefix 여부, enable_prefix_caching |
같은 prefix가 많은 워크로드에서만 의미 있게 비교합니다. |
설정값 선택 기준: 빠르게보다 맞게 고르는 게 먼저입니다
vLLM 옵션은 레버처럼 움직입니다. 하나를 올리면 다른 쪽에서 비용을 냅니다. max_model_len을 키우면 긴 입력을 받을 수 있지만 KV cache 예산이 커지고, max_num_seqs를 키우면 동시 처리 가능성은 늘지만 요청 하나의 지연 시간이 튈 수 있습니다. 그래서 설정은 ‘최대 성능’이 아니라 ‘내 워크로드에 맞는 실패 방식’을 고르는 일에 가깝습니다.
| 상황 | 추천 방향 | 피해야 할 선택 |
|---|---|---|
| 개인 실험, 단일 사용자 | 작은 max_num_seqs, 필요한 만큼의 max_model_len, 단일 요청 TTFT 확인 |
운영도 안 할 긴 컨텍스트를 크게 열어 메모리를 낭비하는 것 |
| 짧은 채팅을 여러 사용자가 사용 | 동시성 단계 테스트, p95 TTFT 기준 운영선 설정 | 평균 tokens/sec만 보고 동시성 상한을 높게 잡는 것 |
| 긴 문서 요약 | 입력 길이 버킷별 측정, max_model_len 보수적 설정, 긴 요청 별도 큐 고려 |
짧은 프롬프트 벤치마크 결과를 긴 문서 처리에 적용하는 것 |
| 동일 시스템 프롬프트 반복 | enable_prefix_caching 효과를 별도 실험 |
prefix가 매번 다른데 캐시 효과를 기대하는 것 |
| 한 GPU에 다른 프로세스도 실행 | gpu_memory_utilization을 낮춰 여유 확보 |
벤치마크 순간 최대 처리량만 보고 메모리를 끝까지 쓰는 것 |
제 기준의 기본값은 이렇습니다. 처음에는 max_model_len을 실제 요청 p95보다 약간 큰 수준으로 잡고, gpu_memory_utilization은 여유를 남긴 값에서 시작합니다. 그다음 동시성을 올리며 p95 TTFT와 실패율을 봅니다. 처리량이 조금 더 나오더라도 p95가 급격히 튀는 지점은 운영선으로 잡지 않습니다.
자주 묻는 질문
Q. vLLM만 쓰면 무조건 빨라지나요?
아닙니다. vLLM은 서버형 추론, 특히 동시 요청 처리에서 강점이 큽니다. 하지만 단일 요청을 짧게 돌리는 실험에서는 차이가 작을 수 있고, 모델이 GPU 메모리에 빠듯하게 올라가는 환경에서는 설정을 잘못 잡으면 오히려 불안정해질 수 있습니다.
Q. 로컬 LLM 성능 비교에서 가장 먼저 볼 지표는 뭔가요?
채팅 UX라면 TTFT와 p95 TTFT를 먼저 봅니다. 배치 요약이나 내부 자동화라면 E2E latency와 처리량을 더 봅니다. 여러 사용자가 붙는 서비스라면 평균보다 실패율과 p95가 우선입니다.
Q. 블로그에 있는 벤치마크 숫자를 믿어도 되나요?
참고는 됩니다. 다만 그대로 의사결정에 쓰면 위험합니다. GPU, 드라이버, vLLM 버전, 모델 revision, 입력 길이, 출력 길이, 동시성, 스트리밍 여부가 다르면 결과가 바뀝니다. 같은 절차로 내 장비에서 다시 재는 것이 가장 확실합니다.
Q. 양자화 모델은 항상 좋은 선택인가요?
메모리 절감에는 유리할 수 있지만 품질, 지원 커널, decode 성능, 운영 안정성을 함께 봐야 합니다. ‘올라간다’와 ‘서비스하기 좋다’는 다릅니다. 양자화 여부를 바꿀 때는 모델 품질 평가와 추론 성능 평가를 분리하세요.
Q. GPU 사용률이 낮으면 vLLM 설정 문제인가요?
항상 그렇지는 않습니다. 클라이언트가 요청을 충분히 못 넣거나, CPU 토크나이징이 막히거나, 네트워크/HTTP 연결이 병목일 수 있습니다. 서버 옵션을 바꾸기 전에 클라이언트가 실제로 원하는 동시성을 만들고 있는지 확인하는 편이 빠릅니다.
마지막 판단: 운영선은 가장 빠른 지점이 아니라 덜 흔들리는 지점입니다
로컬 LLM 성능에서 중요한 질문은 ‘어떤 모델이 제일 빠른가’가 아니라 ‘내 서버가 어떤 요청 분포까지 예측 가능하게 버티는가’입니다. 저는 단일 최고 점수보다 반복 측정에서 비슷하게 나오는 구간을 더 신뢰합니다. 운영자는 평균값이 아니라 나쁜 날의 p95를 상대해야 하니까요.
개인 실험용이면 작은 모델부터 시작해 TTFT와 GPU 메모리 여유를 확인하세요. 여러 사용자가 붙는 내부 서비스라면 vLLM 서버를 띄운 뒤 동시성별 p50/p95 TTFT, E2E latency, tokens/sec, 실패율을 함께 기록하세요. 긴 문서 요약처럼 입력이 긴 워크로드라면 짧은 채팅 벤치마크를 버리고 입력 길이 버킷을 따로 만들어야 합니다.
제가 실제로 권하는 절차는 단순합니다. 워밍업을 제외하고, 같은 프롬프트 세트와 같은 출력 길이로, 동시성을 1에서 시작해 단계적으로 올립니다. 처리량이 더 늘지 않거나 p95 지연 시간이 갑자기 커지거나 실패가 섞이는 지점이 나오면, 그 직전 단계를 운영 후보로 잡습니다. 그다음 max_model_len, max_num_seqs, gpu_memory_utilization을 하나씩만 바꿔 다시 측정합니다.
다음 글에서는 Prometheus와 Grafana를 붙여 vLLM 추론 서버의 요청 지표, GPU 메모리, 지연 시간 분포를 시각화하는 방법을 다룰 예정입니다. 관련 글로는 GPU 모니터링, 모델 서빙 아키텍처, LLM 비용 최적화 글을 함께 연결하면 독자가 다음 단계로 넘어가기 좋습니다. 벤치마크는 한 번 찍는 스크린샷이 아니라 운영 기준을 만드는 과정입니다.
TTFT, 처리량, 동시성, GPU 메모리 기준으로 로컬 LLM 성능 비교 방법을 요약한 인포그래픽입니다.
제 기준의 한 줄 권고는 이렇습니다. 빠른 숫자 하나를 찾지 말고, 같은 조건에서 동시성을 올리며 p95 지연 시간과 실패율이 꺾이는 지점을 찾으세요. 그 직전이 여러분 로컬 LLM 서버의 현실적인 운영선입니다.
![[AI] 로컬 LLM 성능: vLLM 벤치마크 측정 방법론](https://blog.pswq.net/wp-content/uploads/2026/09/local-llm-inference-performance-measurement-vllm-methodology-thumbnail.jpg)
![[Linux] ext4 디스크 I/O 성능 저하 원인 분석 및 진단 방법](https://blog.pswq.net/wp-content/uploads/2026/09/ext4-disk-io-performance-troubleshooting-analysis-thumbnail.jpg)


![[Linux] Linux namespace 활용 사례: 격리 환경 구축과 보안 강화 실전](https://blog.pswq.net/wp-content/uploads/2026/09/linux-namespace-use-cases-practical-isolated-environment-security-thumbnail.jpg)




![[AI] AI 모델 클라우드 마이그레이션 체크리스트: 온프레미스 AI 전환 기준](https://blog.pswq.net/wp-content/uploads/2026/08/onpremise-ai-cloud-migration-checklist-thumbnail.jpg)




![[Nas] Btrfs Proxmox NAS 구축 사례: 성능보다 복구와 스냅샷 운영](https://blog.pswq.net/wp-content/uploads/2026/08/proxmox-btrfs-nas-setup-case-study-performance-snapshot-thumbnail.jpg)




![[Cloud] Jenkins 장애 해결: CI/CD 파이프라인 디버깅 방법론](https://blog.pswq.net/wp-content/uploads/2026/08/jenkins-ci-cd-pipeline-troubleshooting-methodology-thumbnail.jpg)





![[AI] LLM 추론 성능 벤치마크: TensorRT vs ONNX Runtime](https://blog.pswq.net/wp-content/uploads/2026/08/nvidia-tensorrt-onnx-runtime-llm-inference-benchmark-thumbnail.jpg)





![[인프라] Sentry 비용 최적화, 1년 운영 회고로 정리한 현실 전략](https://blog.pswq.net/wp-content/uploads/2026/08/sentry-one-year-retrospective-cost-optimization-thumbnail.jpg)




![[Cloud] Sentry 메모리 누수 해결: 실전 디버깅 사례 연구](https://blog.pswq.net/wp-content/uploads/2026/08/sentry-production-memory-leak-debugging-case-study-thumbnail.jpg)




![[k8s] Karpenter 오토스케일링, 성능 벤치마크: 다양한 워크로드 환경 테스트](https://blog.pswq.net/wp-content/uploads/2026/08/karpenter-autoscaling-performance-benchmark-thumbnail.jpg)



