본문 바로가기
삵
AI

SGLang과 vLLM 중 무엇을 쓸까

냉냉장고를사다줘·2026년 11월 3일·조회 0

요즘 LLM 서빙 엔진을 고르다 보면 거의 vLLM과 SGLang 둘로 좁혀진다. 예전에 vLLM EAGLE3로 처리량 끌어올리는 글을 쓴 적이 있는데, 그때 댓글이고 사내 메신저고 "RAG 챗봇은 SGLang이 더 빠르던데"라는 얘기가 꽤 나왔다. 실제로 멀티턴 대화나 긴 컨텍스트를 반복해서 던지는 서비스라면 두 엔진의 체감 차이가 분명히 있다. 그래서 어떤 경우에 뭘 고를지 정리해 둔다.

결론부터 말하자면, 같은 프롬프트의 앞부분(prefix)이 요청마다 반복되는 워크로드라면 SGLang의 RadixAttention이 KV 캐시를 재사용해 첫 토큰까지 걸리는 시간(TTFT)을 눈에 띄게 줄여준다. 반대로 매번 겹치지 않는 단발성 프롬프트를 쏟아내는 배치성 작업이라면 vLLM이 성숙한 스케줄러와 넓은 하드웨어 호환으로 더 안정적이다. 두 엔진 모두 paged 방식으로 KV 메모리를 쪼개 쓰고 OpenAI 호환 API를 내주지만, 캐시 재사용을 설계의 중심에 둔 쪽은 SGLang이다.

1. RadixAttention이 vLLM과 갈리는 지점

TTFT는 요청을 받고 첫 토큰이 나오기까지의 지연이다. 이 시간의 대부분은 입력 프롬프트 전체를 한 번 통과시키는 prefill 단계가 잡아먹는다. 프롬프트가 길수록 prefill이 무거워지고 TTFT가 늘어난다.

여기서 핵심이 KV 캐시 재사용이다. 멀티턴 대화는 이전 대화 내역이 다음 요청의 앞부분에 그대로 다시 들어간다. RAG도 시스템 프롬프트와 검색된 문서 블록이 요청마다 반복된다. 이 반복되는 앞부분을 매번 새로 prefill하지 않고 지난 요청의 KV 캐시를 그대로 가져다 쓰면 prefill 양이 줄어 TTFT가 떨어진다. 이게 prefix caching이다.

SGLang은 이걸 RadixAttention으로 구현한다. LMSYS 공식 블로그 설명으로는 "프롬프트와 생성 결과의 KV 캐시를 radix tree에 보관"해서 "효율적인 prefix 검색, 삽입, 제거"를 한다. 쉽게 말해 들어온 요청들의 토큰 시퀀스를 트리로 쌓아두고, 새 요청이 들어오면 가장 긴 공통 앞부분을 찾아 그 지점까지의 캐시를 재사용한다. 캐시가 가득 차면 LRU 방식으로 오래된 가지를 쳐낸다. 블로그는 이 덕에 "first token latency에서 특히 뛰어나다"고만 밝히고 구체적 절감률은 적지 않는다. 그러니 몇 퍼센트라고 외우지 말고 본인 워크로드로 직접 재는 게 맞다.

vLLM의 뿌리는 PagedAttention이다. KV 캐시를 OS 가상 메모리처럼 고정 크기 블록으로 쪼개 관리해 메모리 낭비를 줄이고 배치 처리량을 끌어올린다. vLLM도 prefix caching 자체는 지원하고, 최근 버전에서는 기본으로 켜져 블록 해시로 겹치는 앞부분을 재사용한다. 다만 radix tree로 임의 길이의 공통 prefix를 유연하게 맞추고 제거까지 묶어 처리하는 SGLang 쪽이 재사용 설계가 더 공격적이다.

2. 두 엔진 서버 띄우기

H100 한 장에 같은 모델을 올려놓고 비교한다. 먼저 vLLM. OpenAI 호환 서버가 8000 포트로 뜬다.

docker run --gpus all -p 8000:8000 \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  vllm/vllm-openai:latest \
  --model meta-llama/Llama-3.1-8B-Instruct

뜨고 나면 확인.

curl http://localhost:8000/v1/models
{"object":"list","data":[{"id":"meta-llama/Llama-3.1-8B-Instruct","object":"model",...}]}

SGLang은 30000 포트가 기본이다. RadixAttention은 켜달라고 안 해도 기본 활성화 상태다. 끄려면 --disable-radix-cache를 주면 되는데, 비교 실험 때 이 플래그를 넣었다 뺐다 하면 캐시 효과를 바로 체감할 수 있다.

docker run --gpus all -p 30000:30000 \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  lmsysorg/sglang:latest \
  python3 -m sglang.launch_server \
  --model-path meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 --port 30000

SGLang도 OpenAI 호환 엔드포인트를 /v1로 내준다.

curl http://localhost:30000/v1/models
{"object":"list","data":[{"id":"meta-llama/Llama-3.1-8B-Instruct",...}]}

여기서 한 번 걸리는 지점이 모델 접근 권한이다. Llama 계열은 Hugging Face에서 라이선스 동의를 먼저 받아야 받아지므로, 컨테이너에 HF_TOKEN 환경 변수를 넣어주지 않으면 401로 떨어진다. 사내 테스트라면 게이트가 없는 Qwen/Qwen2.5-7B-Instruct 같은 모델로 돌리는 게 번거로움이 적다.

3. TTFT를 직접 재보기

두 엔진 다 벤치마크 도구를 내장하고 있어서 같은 ShareGPT 대화 데이터로 TTFT와 처리량을 잰다. SGLang 쪽 bench_serving은 sharegpt를 지정하면 데이터셋을 자동으로 받아온다.

python3 -m sglang.bench_serving \
  --backend sglang \
  --dataset-name sharegpt \
  --num-prompts 500

============ Serving Benchmark Result ============
Successful requests:                     500
Mean TTFT (ms):                          [환경에 따라 다름]
Median TTFT (ms):                        [환경에 따라 다름]
Output token throughput (tok/s):         ...
==================================================

vLLM은 vllm bench serve를 쓰는데, sharegpt 데이터셋은 --dataset-path로 json 파일 경로를 직접 넘겨야 한다. 경로를 빼면 재현이 안 되니 먼저 받아둔다.

wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json

vllm bench serve \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name sharegpt \
  --dataset-path ./ShareGPT_V3_unfiltered_cleaned_split.json \
  --num-prompts 500

참고로 --num-prompts 기본값은 1000이라, 빠르게 돌리려면 위처럼 줄여서 지정한다.

여기서 숫자 하나만 보고 판단하면 함정에 빠진다. ShareGPT 기본 세트는 서로 다른 대화가 섞여 있어 prefix 재사용 여지가 적다. RadixAttention의 이점은 같은 앞부분이 반복될 때 드러나므로, 실제 서비스 패턴을 흉내 내야 한다. 공통 시스템 프롬프트를 길게 붙인 요청을 반복하거나, 같은 대화를 턴을 늘려가며 재전송하는 식으로 데이터를 구성해 캐시 적중 상황을 만들어야 SGLang의 TTFT 하락이 보인다. 캐시가 거의 안 맞는 데이터로 재면 두 엔진 차이가 미미하거나 오히려 vLLM이 앞설 수도 있다.

SGLang에서 --disable-radix-cache를 켠 상태와 끈 상태를 같은 데이터로 두 번 재서 TTFT 차이를 보면, 캐시 재사용이 실제로 얼마나 기여하는지 본인 환경 숫자로 바로 확인된다.

4. 워크로드로 갈라서 고르기

멀티턴 챗봇, 롱컨텍스트 RAG, 에이전트처럼 요청마다 긴 앞부분이 반복되는 서비스는 SGLang을 먼저 올려본다. 시스템 프롬프트와 대화 내역, 검색 문서가 캐시로 재사용되면서 TTFT가 떨어지고, 첫 응답이 빨라지면 사용자 체감이 눈에 띄게 좋아진다. 구조화된 출력(JSON 강제)이나 복잡한 프롬프트 제어가 필요한 에이전트 워크로드에서도 SGLang의 프런트엔드 기능이 손에 잡힌다.

반대로 매 요청이 제각각인 대량 오프라인 추론, 문서 요약 배치, 임베딩성 단발 프롬프트라면 vLLM이 낫다. 캐시 적중이 거의 없으니 RadixAttention의 이점이 사라지고, 이때는 vLLM의 연속 배치 스케줄러와 넓은 GPU 호환, 성숙한 생태계가 운영 리스크를 줄여준다. 지원 하드웨어 범위나 양자화, 분산 서빙 옵션의 폭도 vLLM이 아직 두텁다.

둘 중 하나로 표준을 못 박기 전에, 실제 트래픽 샘플을 로그에서 뽑아 위 벤치마크로 양쪽을 돌려보고 TTFT와 처리량을 비교하는 걸 권한다. 특히 prefix 재사용률이 얼마나 되는 트래픽인지부터 재보면, 어느 엔진이 유리한지가 숫자로 먼저 갈린다.

자주 묻는 질문

RadixAttention과 vLLM의 prefix caching은 뭐가 다른가

둘 다 반복되는 프롬프트 앞부분의 KV 캐시를 재사용한다는 목표는 같다. vLLM은 KV를 고정 크기 블록으로 나눠 블록 해시로 겹치는 부분을 찾고, 최근 버전은 이 prefix caching을 기본으로 켠다. SGLang의 RadixAttention은 토큰 시퀀스를 radix tree에 쌓아 임의 길이의 공통 앞부분을 유연하게 매칭하고 LRU로 제거까지 묶어 처리한다. 캐시 재사용을 설계의 중심에 둔 쪽이 SGLang이다.

RadixAttention이 TTFT를 몇 퍼센트 줄여주나

공식 LMSYS 블로그는 first token latency에서 특히 유리하다고 정성적으로만 밝히고 구체적 절감률은 제시하지 않는다. 절감폭은 모델, 프롬프트 길이, prefix 재사용률, 하드웨어에 따라 크게 달라지므로 고정된 퍼센트로 외우면 안 된다. sglang.bench_serving으로 --disable-radix-cache를 켠 상태와 끈 상태를 같은 데이터로 재서 본인 환경의 실제 차이를 확인하는 게 맞다.

단발성 프롬프트에서는 왜 vLLM이 유리한가

요청마다 겹치는 앞부분이 없으면 캐시 적중이 거의 발생하지 않아 RadixAttention의 이점이 사라진다. 이 경우 성숙한 연속 배치 스케줄러, 넓은 GPU 호환, 두터운 양자화와 분산 서빙 옵션을 갖춘 vLLM이 대량 오프라인 추론이나 요약 배치에서 더 안정적이다.

벤치마크할 때 왜 기본 ShareGPT 데이터로는 차이가 안 보이나

기본 ShareGPT 세트는 서로 다른 대화가 섞여 있어 prefix 재사용 여지가 적다. RadixAttention은 같은 앞부분이 반복될 때 효과가 나오므로, 공통 시스템 프롬프트를 길게 붙여 반복하거나 같은 대화를 턴을 늘려 재전송하는 식으로 캐시 적중 상황을 만들어야 TTFT 하락이 드러난다.

vllm bench serve로 sharegpt를 돌리면 에러가 나는 이유는

vllm bench serve는 --dataset-name sharegpt를 쓸 때 --dataset-path로 ShareGPT json 파일 경로를 직접 넘겨야 한다. 경로를 빼면 데이터셋을 못 찾아 실패할 수 있다. 먼저 ShareGPT json을 내려받아 경로를 지정하면 재현된다. SGLang의 bench_serving은 sharegpt를 자동으로 받아오는 점이 다르다.

관련 글

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.