Skip to content
Published on

LLM 추론 최적화: vLLM과 TensorRT-LLM 심층 분석

Authors
  • Name
    Twitter

1. LLM 추론의 특수성

Large Language Model(LLM)의 추론(Inference)은 전통적인 딥러닝 모델의 추론과 근본적으로 다른 특성을 갖는다. 이 차이를 이해하는 것이 최적화의 출발점이다.

1.1 Autoregressive Decoding

LLM은 Autoregressive(자기회귀) 방식으로 토큰을 생성한다. 즉, 이전에 생성된 모든 토큰을 입력으로 사용하여 다음 토큰 하나를 예측하고, 이 과정을 End-of-Sequence(EOS) 토큰이 나올 때까지 반복한다. 이는 이미지 분류나 Object Detection처럼 입력을 한 번 Forward Pass하면 결과가 나오는 모델과 본질적으로 다르다.

Input:  "오늘 날씨가"
Step 1: "오늘 날씨가""좋"
Step 2: "오늘 날씨가 좋""습"
Step 3: "오늘 날씨가 좋습""니"
Step 4: "오늘 날씨가 좋습니""다"
Step 5: "오늘 날씨가 좋습니다"[EOS]

이 반복적 특성 때문에 LLM 추론은 Memory-Bound 작업이 된다. 각 스텝에서 수십억 개의 모델 파라미터를 GPU 메모리에서 읽어야 하지만, 실제 연산량(토큰 1개에 대한 행렬곱)은 상대적으로 적다. GPU의 연산 능력(FLOPS)보다 메모리 대역폭(Memory Bandwidth)이 병목이 되는 것이다.

1.2 Prefill Phase vs Decode Phase

LLM 추론은 두 개의 명확히 구분되는 단계로 나뉜다.

Prefill Phase (Prompt Processing)

사용자가 입력한 Prompt 전체를 한 번에 처리하는 단계다. 입력 토큰들을 병렬로 처리할 수 있기 때문에 Compute-Bound 특성을 보인다. 이 단계에서 모든 입력 토큰에 대한 Key-Value(KV) 벡터를 계산하여 캐시에 저장한다. 사용자가 첫 번째 토큰을 받기까지의 지연 시간인 **TTFT(Time To First Token)**는 주로 이 단계의 성능에 의해 결정된다.

Decode Phase (Token Generation)

Prefill 이후 토큰을 하나씩 순차적으로 생성하는 단계다. 매 스텝마다 토큰 하나만 처리하므로 연산량이 적고, 모델 가중치를 메모리에서 로드하는 비용이 지배적인 Memory-Bound 작업이 된다. 이 단계의 성능이 전체 **Throughput(처리량)**과 **TPOT(Time Per Output Token)**을 결정한다.

이 두 단계의 연산 특성이 완전히 다르다는 점이 LLM 추론 최적화를 어렵게 만드는 핵심 요인이다. Prefill은 GPU 연산 유닛을 최대한 활용해야 하고, Decode는 메모리 대역폭을 최대한 활용해야 한다.

2. KV Cache 메커니즘과 메모리 문제

2.1 KV Cache란 무엇인가

Autoregressive Decoding에서 매 스텝마다 이전 모든 토큰의 Attention을 처음부터 다시 계산하면 엄청난 중복 연산이 발생한다. 이를 방지하기 위해 이전 스텝에서 계산한 KeyValue 벡터를 GPU 메모리에 캐싱하는 것이 KV Cache다.

Transformer의 각 Layer에서 Self-Attention은 Query(Q), Key(K), Value(V) 세 가지 벡터를 사용한다. 새로운 토큰을 생성할 때, 해당 토큰의 Q 벡터만 새로 계산하고, 이전 토큰들의 K, V 벡터는 캐시에서 가져온다. 이를 통해 연산량을 O(n^2)에서 O(n)으로 줄일 수 있다.

2.2 KV Cache의 메모리 소비

KV Cache의 메모리 사용량은 다음과 같이 계산된다.

KV Cache Memory = 2 x num_layers x num_heads x head_dim x seq_len x batch_size x dtype_size

예를 들어, Llama-2-70B 모델(80 Layers, 64 KV Heads, Head Dim 128)에서 FP16 기준으로 하나의 시퀀스(seq_len=2048)에 대한 KV Cache는 아래와 같다.

2 x 80 x 64 x 128 x 2048 x 2 bytes =5.24 GB

단 하나의 시퀀스에 5GB 이상이 필요하다. Batch Size를 키우면 이 값이 선형으로 증가하므로, KV Cache는 GPU 메모리의 가장 큰 소비자가 된다. 80GB A100 GPU에서 모델 가중치에 약 35GB(FP16 기준 70B 모델)를 사용하면, KV Cache에 할당할 수 있는 메모리는 약 40GB 남짓이고, 이것이 동시 처리 가능한 요청 수(Batch Size)를 직접적으로 제한한다.

2.3 기존 시스템의 메모리 낭비

기존 LLM Serving 시스템은 KV Cache를 위해 시퀀스의 최대 길이만큼 연속된 메모리를 미리 할당했다. 이 방식에는 세 가지 심각한 비효율이 있다.

  • Internal Fragmentation: 실제 시퀀스 길이가 최대 길이보다 짧으면, 나머지 공간이 낭비된다.
  • External Fragmentation: 다양한 크기의 메모리 블록이 할당/해제되면서 사용 가능하지만 연속되지 않은 메모리 조각이 생긴다.
  • Reservation Waste: 생성이 완료되기 전까지 최대 길이만큼 예약하므로, 실제 사용되지 않는 메모리가 잠겨 있다.

vLLM 논문에 따르면, 기존 시스템에서 KV Cache 메모리의 60~80%가 낭비되고 있었다. 이것이 PagedAttention이 해결하고자 한 핵심 문제다.

3. PagedAttention 상세 분석

3.1 핵심 아이디어: 운영체제의 가상 메모리

PagedAttention는 UC Berkeley의 Woosuk Kwon 등이 2023년 SOSP에서 발표한 논문 *"Efficient Memory Management for Large Language Model Serving with PagedAttention"*에서 제안된 기술이다. 핵심 아이디어는 운영체제(OS)의 Virtual Memory와 Paging 기법을 KV Cache 관리에 적용하는 것이다.

OS에서 프로세스의 가상 주소 공간은 연속적이지만, 실제 물리 메모리(RAM)에서는 고정 크기의 Page 단위로 분산 저장된다. Page Table이 가상 주소를 물리 주소로 매핑한다. PagedAttention은 이 개념을 그대로 KV Cache에 적용한다.

3.2 Block 기반 KV Cache 관리

PagedAttention은 KV Cache를 고정 크기의 Block으로 나눈다. 각 Block은 고정된 수의 토큰(예: 16개)에 대한 Key와 Value 벡터를 저장한다. 핵심 메커니즘은 다음과 같다.

  1. 비연속 메모리 할당: Block들은 GPU 메모리의 어디에나 위치할 수 있다. 연속된 메모리 공간이 필요 없다.
  2. Block Table: 각 시퀀스는 자신의 논리적 Block 번호를 물리적 Block 위치로 매핑하는 Block Table을 가진다. OS의 Page Table과 동일한 개념이다.
  3. 동적 할당: Block은 토큰이 생성될 때 필요에 따라 하나씩 할당된다. 시퀀스의 최대 길이를 미리 예약할 필요가 없다.
  4. 즉시 해제: 시퀀스 생성이 완료되면 해당 Block들이 즉시 Free List로 반환된다.
Sequence "오늘 날씨가 좋습니다":
  Logical Block 0Physical Block 7  (토큰: "오늘", "날씨가", "좋습니다")
  Logical Block 1Physical Block 2  (토큰: ".", [padding])

Block Table:
  [0]7
  [1]2

3.3 메모리 효율 개선

PagedAttention은 기존 시스템 대비 메모리 낭비를 거의 제거한다.

  • Internal Fragmentation: 마지막 Block에서만 발생하며, 평균적으로 Block Size의 절반 미만이다.
  • External Fragmentation: 모든 Block이 동일 크기이므로 완전히 제거된다.
  • Reservation Waste: 필요할 때만 Block을 할당하므로 제거된다.

vLLM 공식 문서에 따르면, PagedAttention을 통해 KV Cache 메모리를 거의 최적에 가깝게 사용할 수 있으며, 이는 동일 GPU에서 처리 가능한 Batch Size를 2~4배 이상 늘리는 효과를 가져온다. Batch Size의 증가는 곧 Throughput의 증가를 의미한다.

3.4 Memory Sharing

PagedAttention의 또 다른 장점은 메모리 공유다. Parallel Sampling이나 Beam Search에서 동일한 Prompt를 공유하는 여러 시퀀스가 있을 때, Prompt에 해당하는 KV Cache Block을 복사하지 않고 Block Table 수준에서 공유할 수 있다. Copy-on-Write(CoW) 방식을 사용하여 시퀀스가 분기되는 시점에서만 새 Block을 할당한다. 이를 통해 Beam Search에서 메모리 사용량을 최대 55%까지 절감할 수 있다.

4. Continuous Batching vs Static Batching

4.1 Static Batching의 한계

전통적인 Static Batching은 여러 요청을 하나의 Batch로 묶어 동시에 처리하되, Batch 내 모든 시퀀스가 완료될 때까지 기다린 후 다음 Batch를 처리한다. 문제는 LLM 응답 길이가 요청마다 크게 다르다는 것이다.

예를 들어 Batch Size 4로 처리할 때, 한 요청은 10 토큰만 생성하고 다른 요청은 500 토큰을 생성한다면, 10 토큰 요청이 끝난 후에도 500 토큰 요청이 완료될 때까지 해당 슬롯의 GPU 자원이 유휴 상태로 남는다. 이 비효율이 실 서비스 환경에서 Throughput을 크게 저하시킨다.

4.2 Continuous Batching (Iteration-Level Scheduling)

Continuous Batching은 스텝(Iteration) 단위로 Scheduling을 수행한다. 핵심 동작 방식은 다음과 같다.

  1. 들어오는 요청들을 대기 큐(Waiting Queue)에 넣는다.
  2. 매 Forward Pass(Generation Step)마다 Scheduler가 대기 큐를 확인한다.
  3. 현재 실행 중인 Batch에 여유 용량이 있으면, 대기 중인 새 요청을 Batch에 추가한다.
  4. GPU가 현재 Batch의 모든 시퀀스에 대해 한 스텝의 Decoding을 수행한다.
  5. 어떤 시퀀스가 생성을 완료하면(EOS 도달), 즉시 해당 슬롯의 리소스를 해제한다.
  6. 다음 스텝에서 빈 슬롯에 새 요청을 채워 넣는다.
Static Batching:
  Step 1: [A, B, C, D]4개 동시 처리
  Step 2: [A, B, C, D]
  Step 3: [A, _, C, D]B 완료, 하지만 슬롯 비어있음
  Step 4: [A, _, _, D]C 완료, 여전히 대기
  Step 5: [_, _, _, D]D 완료까지 대기 후 새 Batch 시작

Continuous Batching:
  Step 1: [A, B, C, D]
  Step 2: [A, B, C, D]
  Step 3: [A, E, C, D]B 완료 즉시 E 투입
  Step 4: [A, E, F, D]C 완료 즉시 F 투입
  Step 5: [G, E, F, D]A 완료 즉시 G 투입

Anyscale의 벤치마크에 따르면, Continuous Batching은 Static Batching 대비 최대 23배의 Throughput 향상을 달성할 수 있다. GPU가 항상 최대한의 시퀀스를 처리하도록 유지하기 때문이다.

4.3 PagedAttention과의 시너지

Continuous Batching이 효과적으로 동작하려면, 다양한 길이의 시퀀스가 동적으로 들어오고 나가는 상황에서 KV Cache 메모리를 유연하게 관리해야 한다. PagedAttention은 Block 단위의 세밀한 메모리 할당/해제를 제공하므로, Continuous Batching의 동적 Scheduling과 완벽히 결합된다. 이 두 기술의 결합이 vLLM의 핵심 경쟁력이다.

5. vLLM 설치 및 OpenAI 호환 API 서버 구축

5.1 설치

vLLM 공식 문서에 따르면, pip을 통해 간단히 설치할 수 있다. Python 3.9 이상과 CUDA 12.x가 필요하다.

# pip을 이용한 설치 (권장)
pip install vllm

# uv를 사용한 환경 관리 (공식 권장)
uv pip install vllm

Docker를 사용하는 경우 vLLM에서 제공하는 공식 이미지를 사용할 수 있다.

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

5.2 Offline Inference (Batch Processing)

단순 배치 추론은 LLM 클래스를 사용한다.

from vllm import LLM, SamplingParams

# 모델 로드
llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct")

# Sampling 파라미터 설정
sampling_params = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=256)

# 배치 추론 실행
prompts = [
    "LLM 추론 최적화의 핵심 기술은",
    "PagedAttention의 원리를 설명하면",
]
outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    print(f"Prompt: {output.prompt}")
    print(f"Generated: {output.outputs[0].text}")

5.3 OpenAI 호환 API 서버

vLLM은 OpenAI API와 호환되는 HTTP 서버를 제공한다. vllm serve 명령어로 실행할 수 있다.

# 서버 시작
vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --dtype auto \
    --api-key token-abc123 \
    --host 0.0.0.0 \
    --port 8000 \
    --max-model-len 4096 \
    --gpu-memory-utilization 0.9

서버가 실행되면 OpenAI Python 클라이언트 라이브러리를 그대로 사용할 수 있다.

from openai import OpenAI

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

# Chat Completions API
response = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[
        {"role": "system", "content": "당신은 도움이 되는 AI 어시스턴트입니다."},
        {"role": "user", "content": "PagedAttention이 무엇인가요?"},
    ],
    temperature=0.7,
    max_tokens=512,
)
print(response.choices[0].message.content)

vLLM의 OpenAI 호환 서버는 /v1/completions, /v1/chat/completions, /v1/embeddings 등의 엔드포인트를 지원하며, 기존 OpenAI API를 사용하는 애플리케이션의 코드 변경 없이 백엔드만 교체할 수 있다는 큰 장점이 있다.

5.4 주요 서버 설정 옵션

옵션설명기본값
--modelHuggingFace 모델 이름 또는 경로필수
--dtype데이터 타입 (auto, float16, bfloat16)auto
--max-model-len최대 시퀀스 길이모델 설정
--gpu-memory-utilizationGPU 메모리 사용 비율0.9
--tensor-parallel-sizeTensor Parallelism GPU 수1
--pipeline-parallel-sizePipeline Parallelism 단계 수1
--quantization양자화 방법 (awq, gptq, fp8 등)None
--enable-prefix-cachingPrefix Caching 활성화False
--max-num-seqs최대 동시 처리 시퀀스 수256

6. vLLM 주요 기능

6.1 Speculative Decoding

Speculative Decoding은 작고 빠른 Draft Model이 여러 토큰을 미리 생성(추측)하고, 큰 Target Model이 이를 한 번에 검증하는 기법이다. 검증을 통과한 토큰들은 그대로 채택되고, 틀린 토큰부터 다시 생성한다. 이를 통해 생성 품질을 유지하면서 Inter-Token Latency를 줄일 수 있다.

vLLM 공식 문서에 따르면, 다음과 같은 Speculative Decoding 방식을 지원한다.

  • Draft Model: 작은 모델(예: Llama-68M)을 Draft로 사용하여 여러 토큰을 미리 생성
  • EAGLE: Neural Network 기반의 효율적인 Draft 생성 방식
  • MLP Draft Model: MLP 기반의 경량 Draft 생성
  • N-gram Speculation: 입력 텍스트의 N-gram 패턴을 기반으로 토큰 예측
  • Suffix Decoding: Suffix 기반의 예측 전략
# Draft Model을 사용한 Speculative Decoding 서버 실행
vllm serve meta-llama/Llama-3.1-70B-Instruct \
    --speculative-model meta-llama/Llama-3.2-1B-Instruct \
    --num-speculative-tokens 5 \
    --use-v2-block-manager

Speculative Decoding은 QPS(Queries Per Second)가 낮거나 중간 수준인 Memory-Bound 워크로드에서 특히 효과적이며, 높은 QPS 환경에서는 Draft Model의 추가 연산 부담으로 인해 오히려 성능이 저하될 수 있다.

6.2 Automatic Prefix Caching (APC)

Automatic Prefix Caching은 동일한 Prefix를 가진 요청들이 반복될 때 해당 Prefix의 KV Cache를 재사용하는 기능이다. 예를 들어 System Prompt가 동일한 수천 개의 요청이 들어올 때, System Prompt에 대한 KV Cache를 한 번만 계산하고 이후 요청에서 재활용한다.

vLLM에서 Prefix Caching은 Block 단위로 동작한다. 요청이 들어오면 Prompt의 토큰을 Block 단위로 나누고, 이미 캐시에 존재하는 Block은 재사용하며, 새로운 토큰이 등장하는 지점부터만 새 Block을 생성한다.

# Prefix Caching 활성화
vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --enable-prefix-caching

이 기능은 RAG(Retrieval-Augmented Generation), Few-shot Learning, Multi-turn 대화 등 동일한 Prefix가 반복되는 시나리오에서 TTFT를 대폭 단축시킨다.

6.3 LoRA Serving

vLLM은 하나의 Base Model에 여러 LoRA(Low-Rank Adaptation) Adapter를 동적으로 로드하여 서빙할 수 있다. 이를 통해 다양한 Fine-tuned 모델 변형을 하나의 서버 인스턴스에서 처리할 수 있다.

# LoRA Adapter를 사용한 서버 실행
vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --enable-lora \
    --lora-modules my-adapter=./path/to/lora/adapter \
    --max-lora-rank 64

요청 시 model 파라미터에 LoRA Adapter 이름을 지정하면, 해당 Adapter가 적용된 결과를 반환한다. Tensor Parallelism 환경에서는 기본적으로 LoRA 연산의 절반만 분산 처리되며, --fully-sharded-loras 옵션을 사용하면 긴 시퀀스나 높은 Rank에서 더 나은 성능을 얻을 수 있다.

7. TensorRT-LLM 개요 및 모델 변환

7.1 TensorRT-LLM이란

TensorRT-LLM은 NVIDIA가 개발한 LLM 추론 최적화 라이브러리로, TensorRT 엔진을 LLM에 특화하여 구축한 것이다. Python API를 통해 LLM 모델을 정의하고, NVIDIA GPU에서 최적화된 추론 엔진으로 컴파일한다. 핵심 특징은 다음과 같다.

  • NVIDIA GPU 전용 최적화: CUDA Kernel을 NVIDIA 아키텍처에 맞게 최적화
  • FP8/NVFP4 양자화: Hopper(H100), Ada Lovelace, Blackwell(B200) 아키텍처의 하드웨어 가속 양자화 지원
  • In-flight Batching: Continuous Batching의 TensorRT-LLM 구현
  • Tensor/Pipeline Parallelism: 다중 GPU 분산 추론 지원
  • Paged KV Cache: PagedAttention과 유사한 메모리 관리
  • EAGLE-3 Speculative Decoding: 최신 Speculative Decoding 기법 지원

7.2 모델 변환 Workflow

TensorRT-LLM의 모델 변환은 두 단계로 이루어진다.

Step 1: Checkpoint 변환

HuggingFace 등의 프레임워크 Checkpoint를 TensorRT-LLM 형식으로 변환한다.

# Llama 모델 Checkpoint 변환
python convert_checkpoint.py \
    --model_dir /path/to/Llama-3.1-8B-Instruct \
    --output_dir ./tllm_checkpoint_1gpu \
    --dtype float16

# Tensor Parallelism 적용 시
python convert_checkpoint.py \
    --model_dir /path/to/Llama-3.1-70B-Instruct \
    --output_dir ./tllm_checkpoint_4gpu_tp4 \
    --dtype float16 \
    --tp_size 4

Step 2: TensorRT 엔진 빌드

변환된 Checkpoint를 trtllm-build 명령어로 최적화된 TensorRT 엔진으로 컴파일한다.

trtllm-build \
    --checkpoint_dir ./tllm_checkpoint_1gpu \
    --output_dir ./trt_engines/llama-8b/fp16/1-gpu \
    --gemm_plugin auto \
    --max_batch_size 64 \
    --max_input_len 2048 \
    --max_seq_len 4096

7.3 Python API를 통한 빌드

Python API를 사용하여 프로그래밍 방식으로 변환과 빌드를 수행할 수도 있다.

import tensorrt_llm
from tensorrt_llm import BuildConfig

# HuggingFace 모델에서 직접 변환 및 빌드
llama = tensorrt_llm.LLaMAForCausalLM.from_hugging_face(
    model_dir="/path/to/Llama-3.1-8B-Instruct",
    dtype="float16",
)

# 엔진 빌드
build_config = BuildConfig(max_batch_size=64)
engine = tensorrt_llm.build(llama, build_config)

# 엔진 저장
engine.save("./trt_engines/llama-8b")

이 과정에서 convert_checkpoint.py와 같은 CLI 도구는 내부 API를 사용하므로, TensorRT-LLM 버전과 예제 폴더의 스크립트 버전을 반드시 일치시켜야 한다.

8. 양자화 기법 비교

양자화(Quantization)는 모델의 가중치와 활성화 값을 더 낮은 정밀도로 표현하여 메모리 사용량과 연산량을 줄이는 기법이다. LLM 추론에서 가장 널리 사용되는 양자화 기법을 비교한다.

8.1 GPTQ (Post-Training Quantization)

GPTQ는 Layer별로 Hessian 정보를 활용하여 양자화 오차를 최소화하는 Post-Training Quantization(PTQ) 기법이다.

  • 원리: 각 Layer의 가중치를 양자화할 때, 출력 활성화의 변화를 최소화하도록 보정(Compensation)을 수행한다. 보정 과정에서 Hessian 행렬의 역행렬을 근사하여 최적의 양자화 값을 찾는다.
  • 정밀도: 주로 INT4 Weight-Only (W4A16)
  • 장점: Calibration 데이터셋만 있으면 되며, 학습 없이 양자화 가능
  • 단점: Calibration에 수십 분 소요, AWQ 대비 약간 낮은 정확도 보고

8.2 AWQ (Activation-Aware Weight Quantization)

AWQ는 가중치의 중요도를 활성화(Activation) 분포를 기반으로 판단하여, 중요한 가중치를 보호하는 양자화 기법이다.

  • 원리: 모든 가중치가 동일하게 중요한 것이 아니라, 활성화 크기가 큰 채널에 연결된 가중치가 모델 성능에 더 큰 영향을 미친다. 이 소수의 중요한 가중치(약 1%)를 높은 정밀도로 유지하고, 나머지를 더 공격적으로 양자화한다.
  • 정밀도: INT4 Weight-Only (W4A16) 또는 W4A8
  • 장점: GPTQ 대비 높은 정확도 유지(약 95% Quality Retention), 하드웨어 효율적
  • 단점: Calibration 과정 필요

8.3 SqueezeLLM

SqueezeLLM은 UC Berkeley에서 제안한 기법으로, 비균일(Non-uniform) 양자화Dense-and-Sparse 분해를 결합한다.

  • 원리: 각 출력 채널에 별도의 Lookup Table을 할당하여, 채널 단위의 비균일 양자화를 수행한다. 또한 Outlier 가중치를 Sparse 행렬로 분리하여 별도 처리한다. 기존 GPTQ/AWQ가 개별 Layer의 출력 변화를 최소화하는 것과 달리, SqueezeLLM은 모델 최종 출력의 변화를 최소화하도록 최적화한다.
  • 정밀도: INT3, INT4 수준
  • 장점: 극도의 저비트 양자화에서도 높은 정확도 유지
  • 단점: 구현 복잡도가 높고, 추론 시 Lookup Table 연산 오버헤드 존재

8.4 FP8 양자화

FP8(8-bit Floating Point)은 NVIDIA Hopper(H100) 이후 아키텍처에서 하드웨어 수준으로 지원되는 양자화 형식이다.

  • 원리: FP16/BF16 가중치와 활성화를 8비트 부동소수점(E4M3 또는 E5M2)으로 변환한다. Tensor Core가 FP8 연산을 네이티브로 지원하므로, INT8과 달리 별도의 역양자화(Dequantization) 오버헤드가 거의 없다.
  • 정밀도: W8A8 (가중치와 활성화 모두 8비트)
  • 장점: 가장 높은 정확도 유지, 가장 빠른 Calibration(분 단위), H100/B200에서 하드웨어 가속
  • TensorRT-LLM 기준 성능: Llama-v2-7B에서 FP16 대비 Batch Size 1에서 1.51배, Batch Size 8에서 1.40배 속도 향상 (NVIDIA 공식 벤치마크)

8.5 양자화 선택 가이드

TensorRT-LLM 공식 문서의 권장 사항은 다음과 같다.

시나리오권장 방식이유
Small Batch (BS 4 이하)Weight-Only (W4A16, W8A16)Memory Bandwidth가 병목이므로, 가중치 크기를 줄이는 것이 효과적
Large Batch (BS 16 이상)FP8 (W8A8) 우선연산량이 많아지므로 가중치와 활성화 모두 양자화하는 것이 유리
최고 정확도 필요FP8정확도 손실이 가장 적음
최대 압축 필요INT4 AWQ/GPTQ4비트로 모델 크기를 약 75% 줄임
H100/B200 사용FP8 또는 NVFP4하드웨어 네이티브 지원으로 최고 성능

vLLM에서 양자화된 모델을 사용하는 방법은 다음과 같다.

# AWQ 양자화 모델 서빙
vllm serve TheBloke/Llama-2-7B-AWQ \
    --quantization awq \
    --dtype auto

# GPTQ 양자화 모델 서빙
vllm serve TheBloke/Llama-2-7B-GPTQ \
    --quantization gptq \
    --dtype auto

# FP8 양자화 서빙 (H100 이상)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --quantization fp8 \
    --dtype auto

9. 벤치마크 비교: Throughput, Latency, TTFT

9.1 주요 성능 지표

LLM 추론 성능을 평가할 때 사용하는 핵심 지표는 다음과 같다.

  • Throughput: 초당 처리할 수 있는 토큰 수 (tokens/second) 또는 요청 수 (requests/second)
  • Latency: 요청 시작부터 응답 완료까지의 전체 소요 시간
  • TTFT (Time To First Token): 요청 시작부터 첫 번째 토큰이 생성되기까지의 시간. 사용자 체감 응답 속도에 직결
  • TPOT (Time Per Output Token): 첫 번째 토큰 이후 각 토큰이 생성되는 평균 시간. Streaming 출력 속도에 직결
  • ITL (Inter-Token Latency): 토큰 간 생성 간격

9.2 vLLM vs TensorRT-LLM 비교

2025년 다수의 벤치마크 결과를 종합하면, 두 프레임워크는 각기 다른 강점을 보인다.

Throughput

TensorRT-LLM은 전반적으로 가장 높은 Throughput을 기록한다. NVIDIA GPU에 최적화된 CUDA Kernel과 TensorRT 엔진 컴파일 최적화 덕분이다. 약 180220 req/sec 수준이며, vLLM은 120160 req/sec 수준으로 두 번째로 높은 성능을 보인다. 다만 이 수치는 모델, GPU, Batch Size, 시퀀스 길이 등에 따라 크게 달라진다.

TTFT (Time To First Token)

TTFT에서는 vLLM이 우수한 성능을 보인다. 동시 사용자 수가 증가해도 5080ms 수준을 안정적으로 유지한다. TensorRT-LLM은 Low Concurrency에서 3550ms로 더 빠르지만, High Concurrency에서의 Scaling 특성이 vLLM보다 열세인 경우가 보고된다.

Latency

Per-Token Latency는 TensorRT-LLM이 Low Concurrency에서 우세하다. 특히 B200 GPU에서는 TensorRT-LLM이 모든 지표에서 SGLang과 vLLM을 앞선다.

종합 비교

지표vLLMTensorRT-LLM
Throughput높음 (2위)매우 높음 (1위)
TTFT (Low QPS)우수매우 우수
TTFT (High QPS)안정적가변적
설정 난이도낮음높음 (빌드 과정 필요)
모델 호환성매우 넓음NVIDIA GPU 전용
유연성높음 (Python API)중간 (엔진 재빌드 필요)
커뮤니티/생태계매우 활발NVIDIA 주도

9.3 프레임워크 선택 기준

  • 빠른 프로토타이핑과 유연한 서빙: vLLM이 적합하다. pip install 한 줄로 시작할 수 있고, HuggingFace 모델을 직접 로드하여 바로 서빙할 수 있다.
  • 최대 성능이 필요한 프로덕션: TensorRT-LLM이 적합하다. 모델 변환과 엔진 빌드에 시간이 걸리지만, 최적화된 엔진의 성능이 우수하다. 특히 NVIDIA 최신 GPU(H100, B200)에서 FP8/NVFP4를 활용할 때 성능 차이가 극대화된다.
  • 다양한 모델과 빠른 실험: vLLM이 유리하다. LoRA Adapter 교체, 양자화 방식 변경 등이 서버 재시작만으로 가능하다.
  • 최신 NVIDIA GPU 활용 극대화: TensorRT-LLM이 유리하다. Hopper, Blackwell 아키텍처의 하드웨어 기능을 가장 먼저, 가장 깊게 활용한다.

10. SGLang 간략 비교

SGLang(Structured Generation Language)은 UC Berkeley LMSYS에서 개발한 LLM 추론 프레임워크로, vLLM과 TensorRT-LLM에 이어 주목받고 있는 엔진이다.

10.1 핵심 차별점: RadixAttention

SGLang의 핵심 기술은 RadixAttention이다. 이는 Prefix Caching을 Radix Tree(기수 트리) 자료구조로 관리하는 방식으로, vLLM의 Block 단위 Prefix Caching보다 더 세밀한 토큰 수준의 Prefix 매칭과 재사용을 가능하게 한다.

Radix Tree는 여러 요청의 Prefix를 트리 구조로 공유하므로, Multi-turn 대화에서 이전 턴의 KV Cache를 효율적으로 재활용할 수 있다. 벤치마크에서 RadixAttention은 큰 Multi-turn 대화에서 vLLM 대비 약 10%의 성능 향상을 보였다.

10.2 성능 비교

2025년 벤치마크 결과를 종합하면 다음과 같다.

  • Throughput: SGLang은 RadixAttention을 통해 약 16,200 tokens/sec를 달성, vLLM의 약 12,500 tokens/sec 대비 약 29% 높은 Throughput을 기록
  • 전체 처리 시간: 500개의 Prompt 처리에서 SGLang 54.2초, vLLM 58.9초로 SGLang이 약 8% 빠름
  • Multi-turn 시나리오: SGLang이 KV Cache 재사용 효율에서 뚜렷한 우위

10.3 사용 시나리오

시나리오추천 프레임워크
높은 동시성, 단일 턴vLLM
Multi-turn 대화, 구조화된 출력SGLang
NVIDIA GPU 최대 성능TensorRT-LLM
빠른 시작, 넓은 모델 호환vLLM
복잡한 Generation LogicSGLang

SGLang은 특히 복잡한 Multi-turn 상호작용, 구조화된 출력(JSON Schema 등), 그리고 정교한 생성 제어가 필요한 워크로드에서 강점을 보인다. 반면 vLLM은 높은 동시성 환경에서의 단일 턴 처리와 제한된 리소스에서의 Throughput 극대화에 적합하다.

11. 결론

LLM 추론 최적화는 단일 기술이 아닌, 여러 기술의 조합으로 이루어진다. PagedAttention은 KV Cache의 메모리 낭비를 제거하고, Continuous Batching은 GPU 활용률을 극대화하며, 양자화는 모델 크기와 연산량을 줄인다. Speculative Decoding은 Per-Token Latency를 개선하고, Prefix Caching은 반복되는 Prompt의 TTFT를 단축한다.

vLLM은 이러한 기술들을 사용하기 쉬운 인터페이스로 통합하여 빠른 프로토타이핑과 유연한 서빙을 가능하게 하고, TensorRT-LLM은 NVIDIA GPU에 특화된 깊은 최적화를 통해 최대 성능을 추구한다. SGLang은 RadixAttention을 통한 효율적인 KV Cache 재사용으로 Multi-turn 시나리오에서 차별화된 성능을 제공한다.

프레임워크 선택은 서비스 요구사항, 하드웨어 환경, 운영 복잡도를 종합적으로 고려하여 결정해야 한다. 어떤 프레임워크를 선택하든, 본 글에서 다룬 핵심 기술들의 원리를 이해하는 것이 올바른 설정과 튜닝의 기반이 될 것이다.

References