본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
vLLM
KV 캐시를 운영체제의 페이지처럼 블록 단위로 관리하는 페이지드 어텐션(PagedAttention)을 바탕으로 만든 오픈소스 대형 언어 모델 추론·서빙 엔진

vLLM은 UC 버클리 스카이 컴퓨팅 연구실에서 시작한 대형 언어 모델(LLM) 추론·서빙 엔진이다. 2023년 6월 공개 당시 핵심은 페이지드 어텐션이었다. 요청마다 커졌다 줄었다 하는 KV 캐시를 고정 크기 블록으로 쪼개 필요할 때만 할당해 메모리 낭비를 거의 없앴고, 그만큼 더 많은 요청을 한 배치에 묶어 처리량을 끌어올렸다. 이름의 v는 원래 가상 메모리에서 착안해 vCPU처럼 "virtual"을 뜻했다고 논문 제1저자인 Woosuk Kwon이 밝혔다.[1][2][3]

지금은 연속 배칭, 접두사 캐싱, 청크 프리필, 추측 디코딩, 텐서 병렬화, 양자화 모델 실행, 구조화된 출력을 갖춘 범용 엔진이 되었고, OpenAI API와 호환되는 HTTP 서버를 내장해 기존 클라이언트 코드를 그대로 붙일 수 있다. 라이선스는 아파치 2.0이고, 2025년 5월 파이토치 재단(PyTorch Foundation)의 호스팅 프로젝트가 되었다.[4] 2026년 10월 기준 최신 릴리스는 v0.31.0(2026-10-05)이다.[5]

배경: LLM 서빙은 메모리 문제다

편집 원본 편집

자기회귀 생성은 토큰을 하나 만들 때마다 모델 가중치 전체를 읽어야 해서 한 요청만 처리하면 GPU 연산 능력이 거의 놀게 된다. 여러 요청을 한 배치로 묶어야 처리량이 오르는데, 배치 크기를 막는 것이 KV 캐시 메모리다.

  • 크기: OPT-13B는 토큰 하나의 KV 캐시가 2(키·값) × 5120(은닉 차원) × 40(층) × 2바이트(FP16) = 800KB다. 2,048토큰 요청 하나면 최대 1.6GB가 된다. 40GB A100에 13B 모델을 올리면 메모리의 약 65%가 가중치, 30% 가까이가 KV 캐시 몫이다.[3]
  • 낭비: 기존 시스템(FasterTransformer, Orca)은 요청마다 최대 길이만큼 연속된 메모리를 미리 잡았다. 실제 출력이 짧으면 남는 칸(내부 단편화), 요청마다 잡은 크기가 달라 생기는 틈(외부 단편화), 아직 안 쓴 미래 토큰 자리(예약)가 모두 낭비다. 논문의 측정에서 기존 시스템은 KV 캐시 메모리의 20.4~38.2%만 실제 토큰 상태 저장에 썼다.[3]
  • 공유 불가: 한 프롬프트에서 여러 답을 뽑는 병렬 샘플링이나 빔 서치는 프롬프트 부분 KV 캐시가 같은데도, 연속 공간에 따로 저장하니 공유할 수 없었다.

페이지드 어텐션

편집 원본 편집

페이지드 어텐션은 운영체제의 가상 메모리와 페이징을 그대로 빌려 왔다. 블록은 페이지, 토큰은 바이트, 요청은 프로세스에 대응한다.[3]

  • 각 요청의 KV 캐시를 블록 크기 B개 토큰씩 담는 논리 블록으로 나눈다. vLLM의 기본 블록 크기는 16토큰이다.[3]
  • GPU 메모리는 같은 크기의 물리 블록 풀로 미리 나눠 둔다. 요청마다 블록 테이블이 논리 블록 번호를 물리 블록 번호로 대응시킨다. 물리 블록은 연속일 필요가 없다.
  • 토큰이 생성되어 마지막 블록이 다 차면 그때 새 물리 블록 하나를 할당한다. 그래서 요청 하나의 낭비는 마지막 블록의 빈칸뿐이고, 모든 블록 크기가 같으니 외부 단편화가 없다. 공개 블로그는 이 낭비를 4% 미만으로 소개했다.[2]

블록 단위 어텐션 계산

편집 원본 편집

쿼리 qi가 볼 키·값을 블록 Kj=(k(j−1)B+1,…,kjB), Vj로 묶으면 어텐션은 블록 단위 합으로 다시 쓸 수 있다.[3]

Aij=exp⁡(qi⊤Kj/d)∑t=1⌈i/B⌉exp⁡(qi⊤Kt𝟏/d),oi=∑j=1⌈i/B⌉VjAij⊤

커널은 블록 테이블을 따라 흩어진 블록을 하나씩 가져와 계산한다. 수학적으로는 일반 어텐션과 같고, 바뀐 것은 메모리 배치뿐이다.

블록 공유와 쓰기 시 복사

편집 원본 편집

물리 블록마다 참조 횟수를 둔다. 병렬 샘플링에서는 여러 출력 시퀀스가 프롬프트 블록을 함께 가리키고, 어느 시퀀스가 공유 블록에 새 토큰을 써야 할 때만 그 블록을 복사한다(copy-on-write). 빔 서치는 후보들이 갈라지기 전까지의 블록을 대부분 공유한다. 논문은 OPT-13B, Alpaca 데이터에서 병렬 샘플링 6.1~9.8%, 빔 서치 37.6~55.2%의 메모리 절감을 보고했다(ShareGPT 데이터에서는 각각 16.2~30.5%, 44.3~66.3%).[3]

스케줄링과 선점

편집 원본 편집

요청은 선착순(FCFS)으로 받는다. 메모리가 모자라면 가장 늦게 들어온 요청을 선점(preemption)하는데, 한 요청의 블록은 함께 빼낸다. 빼낸 KV 캐시는 CPU 메모리로 옮겨 두었다가 되돌리거나(스와핑), 버렸다가 다시 프리필해서 계산한다(재계산). 블록 크기가 작을 때는 재계산이 스와핑보다 빠를 수 있다.[3]

논문의 성능 수치

편집 원본 편집
비교 조건 결과
기존 최고 시스템 대비 처리량 같은 지연 수준, OPT·LLaMA 모델 2~4배[3]
Orca(Oracle) 대비 감당 가능한 요청률 ShareGPT 데이터 1.7~2.7배
Orca(Max) 대비 ShareGPT 데이터 2.7~8배
FasterTransformer 대비 ShareGPT 데이터 최대 22배
동시 처리 요청 수 OPT-13B, Orca(Oracle) 대비 2.2배
출시 블로그 처리량 LLaMA-7B(A10G), LLaMA-13B(A100 40GB), ShareGPT 허깅 페이스 Transformers 대비 최대 24배, TGI 대비 최대 3.5배[2]

Orca(Oracle)는 실제 출력 길이를 미리 안다고 가정한 상한, Orca(Max)는 최대 길이만큼 예약하는 설정이다. 긴 시퀀스, 큰 모델, 복잡한 디코딩일수록 차이가 커진다.

엔진 구조와 주요 기능

편집 원본 편집

2025년 1월 공개한 V1 엔진은 스케줄러, KV 캐시 관리자, 워커, 샘플러, API 서버를 새로 짰다.[6]

  • 통합 스케줄러: 프리필과 디코드를 따로 구분하지 않고, 스텝마다 {요청 ID: 처리할 토큰 수} 형태로 토큰 예산을 나눈다. 이 표현 하나로 청크 프리필, 접두사 캐싱, 추측 디코딩을 같은 경로에서 처리한다.[6]
  • 자동 접두사 캐싱: 블록마다 "앞 블록의 해시 + 이 블록의 토큰 + 추가 식별자(LoRA ID, 이미지 해시, 캐시 솔트)"로 해시를 만들어, 같은 접두사를 가진 요청이 이미 계산된 블록을 재사용한다. 꽉 찬 블록만 캐시하고, 참조가 끝난 블록은 LRU 순서로 내보낸다.[7] V1에서는 적중률 0%일 때도 처리량 손실이 1% 미만이라 기본으로 켰다.[6] 원리는 프롬프트 캐싱 문서 참고.
  • 청크 프리필: 긴 프롬프트를 토큰 예산(--max-num-batched-tokens)에 맞게 잘라 디코드와 한 배치에 섞는다. 자세한 내용은 연속 배칭 문서 참고.
  • 분산 실행: 텐서 병렬(-tp), 파이프라인 병렬, 전문가 병렬, 프리필·디코드 분리 서빙을 지원한다.[4]
  • 양자화: GPTQ, AWQ, FP8 등 미리 양자화된 체크포인트를 읽고, KV 캐시 자체를 FP8 등으로 저장하는 옵션(--kv-cache-dtype)도 있다.[8]
  • 구조화된 출력: JSON 스키마, 정규 표현식, 선택지, 문법을 지정하면 XGrammar 등의 백엔드로 제약 디코딩을 한다. 자세한 내용은 구조화된 출력 (언어 모델) 문서 참고.
  • 하드웨어: 엔비디아 GPU 외에 AMD GPU, 구글 TPU, 인텔 GPU·CPU, AWS 뉴런 등을 파이토치 백엔드로 지원한다.[4]

오프라인 일괄 추론

편집 원본 편집
from vllm import LLM, SamplingParams

prompts = ["대한민국의 수도는", "트랜스포머의 핵심 아이디어는"]
params = SamplingParams(temperature=0.8, top_p=0.95, max_tokens=64)

llm = LLM(model="Qwen/Qwen2.5-1.5B-Instruct")   # 허깅 페이스 허브 모델 이름
for out in llm.generate(prompts, params):       # 요청을 한꺼번에 넣으면 엔진이 알아서 배칭
    print(out.prompt, "->", out.outputs[0].text)

OpenAI 호환 서버

편집 원본 편집

서버는 vllm serve 명령 하나로 뜬다. 기본 주소는 http://localhost:8000이고 OpenAI 호환 경로는 /v1 아래다(/v1/completions, /v1/chat/completions 등).[9]

pip install vllm

# GPU 2장에 텐서 병렬로 올리고, GPU 메모리의 90%를 엔진에 할당
vllm serve Qwen/Qwen2.5-7B-Instruct \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.90 \
  --max-model-len 32k
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
resp = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[{"role": "user", "content": "페이지드 어텐션을 한 문장으로 설명해 줘."}],
)
print(resp.choices[0].message.content)

자주 만지는 엔진 인자

편집 원본 편집
인자 뜻
--tensor-parallel-size, -tp 텐서 병렬 GPU 수. 기본 1
--gpu-memory-utilization 엔진이 쓸 GPU 메모리 비율(0~1). 가중치를 올리고 남은 몫이 KV 캐시 블록 풀이 된다. 지정하지 않으면 0.92
--max-model-len 프롬프트와 출력을 합친 최대 길이. 줄이면 같은 메모리에 더 많은 요청이 들어간다
--max-num-seqs 한 스텝에 함께 처리할 최대 시퀀스 수
--max-num-batched-tokens 한 스텝의 토큰 예산. 청크 프리필의 조각 크기를 정한다
--enable-prefix-caching / --disable-prefix-caching 접두사 캐싱 켜기·끄기
--quantization, -q 가중치 양자화 방식. 지정하지 않으면 모델 설정 파일의 양자화 정보를 따른다
--kv-cache-dtype KV 캐시 저장 자료형(auto, fp8 등)

값과 기본값은 v0.31 시점 공식 문서 기준이며 버전마다 바뀐다.[8]

다른 서빙 엔진과 비교

편집 원본 편집
항목 vLLM SGLang TensorRT-LLM TGI llama.cpp
개발 UC 버클리 출신, 파이토치 재단 호스팅 LMSYS(비영리) 엔비디아 허깅 페이스 ggml-org 커뮤니티
주 언어 파이썬, CUDA 등 커널 파이썬, CUDA 등 커널 C++, 파이썬(파이토치 기반 API) 러스트(라우터), 파이썬 C/C++
KV 캐시 관리 페이지드 어텐션, 해시 기반 접두사 캐싱 기수 트리로 접두사 공유를 관리하는 RadixAttention[10] 페이지 방식 KV 캐시, 인플라이트 배칭 페이지드 어텐션 사용 슬롯 단위 KV 캐시, 서버에서 병렬 슬롯
주 하드웨어 엔비디아·AMD GPU, TPU, 인텔 등 엔비디아·AMD GPU, TPU, 애플 실리콘 등 엔비디아 GPU 전용 엔비디아·AMD GPU 등 CPU, 애플 실리콘, 각종 GPU
강점 모델 지원 폭, 커뮤니티, OpenAI 호환 서버 접두사 재사용이 많은 에이전트·다회 호출, 구조화 출력 속도 엔비디아 최신 GPU에서 최고 수준 최적화(FP8·FP4) 허깅 페이스 생태계 연동 설치 간단, 낮은 비트 양자화(GGUF), 로컬 실행
상태(2026-10) 활발 활발 활발 유지보수 모드 후 저장소 보관 처리. 허깅 페이스는 vLLM, SGLang 등을 권장[11] 활발
  • SGLang 논문은 에이전트 제어, 퓨샷 벤치마크, JSON 디코딩 등에서 당시 최신 시스템 대비 최대 6.4배 처리량을 보고했다.[10]
  • 고르는 기준: 데이터센터 GPU에서 여러 모델을 범용으로 서빙하려면 vLLM이나 SGLang, 엔비디아 하드웨어에서 마지막 성능까지 짜내려면 TensorRT-LLM, 노트북·CPU·엣지에서 혼자 쓰려면 llama.cpp나 이를 감싼 Ollama가 맞다. 요청 하나의 지연만 보면 엔진 간 차이가 작고, 동시 요청이 많을수록 KV 캐시 관리와 스케줄러 차이가 커진다.
  • 페이지드 어텐션 커널은 블록 테이블을 따라 간접 참조하므로 연속 메모리 커널보다 커널 하나의 비용이 약간 크다. 논문은 이 오버헤드를 처리량 이득이 덮는다고 보았다.[3]
  • 블록 크기가 크면 커널 병렬성은 좋아지지만 마지막 블록 낭비가 늘고, 작으면 그 반대다.
  • 엔진 인자와 API가 빠르게 바뀐다. 예를 들어 구조화된 출력의 guided_json 같은 옛 요청 인자는 v0.12.0에서 제거되고 structured_outputs로 바뀌었다.[12] 운영 환경에서는 버전을 고정하는 편이 안전하다.
  1. ↑ vLLM full name, vllm-project/vllm 이슈 835, GitHub, 2023-08.
  2. ↑ 2.0 2.1 2.2 vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention, vLLM Blog, 2023-06-20.
  3. ↑ 3.00 3.01 3.02 3.03 3.04 3.05 3.06 3.07 3.08 3.09 Woosuk Kwon 외, 〈Efficient Memory Management for Large Language Model Serving with PagedAttention〉, SOSP 2023. arXiv:2309.06180
  4. ↑ 4.0 4.1 4.2 PyTorch Foundation Welcomes vLLM as a Hosted Project, PyTorch Foundation, 2025-05-07.
  5. ↑ vllm-project/vllm, GitHub.
  6. ↑ 6.0 6.1 6.2 vLLM V1: A Major Upgrade to vLLM's Core Architecture, vLLM Blog, 2025-01-27.
  7. ↑ Automatic Prefix Caching, vLLM Documentation.
  8. ↑ 8.0 8.1 Engine Arguments, vLLM Documentation.
  9. ↑ Quickstart, vLLM Documentation.
  10. ↑ 10.0 10.1 Lianmin Zheng 외, 〈SGLang: Efficient Execution of Structured Language Model Programs〉, NeurIPS 2024. arXiv:2312.07104
  11. ↑ huggingface/text-generation-inference, GitHub.
  12. ↑ Structured Outputs, vLLM Documentation.