vLLM으로 로컬 서빙을 붙여 둔 지가 좀 됐는데, 요즘 같은 H100이나 L40S 장비에서 "TensorRT-LLM이 더 빠르다더라"는 얘기를 심심찮게 듣는다. 예전에 TensorRT-LLM을 만져 본 사람이면 trtllm-build로 엔진부터 컴파일하던 기억 때문에 손이 잘 안 갔을 텐데, 1.x로 올라오면서 그 단계가 통째로 사라졌다. 그래서 이번에는 vLLM 대신 trtllm-serve만으로 HuggingFace 체크포인트를 바로 올려 보고, 처리량도 같은 자로 재 본다.
결론부터 말하자면, TensorRT-LLM 1.0 이후로는 PyTorch 백엔드가 기본이고 trtllm-serve <모델> 한 줄이면 포트 8000에 OpenAI 호환 서버가 뜬다. 엔진을 미리 빌드할 필요가 없고(1.2대에서는 trtllm-build 자체가 서빙 경로에서 빠졌다), vLLM 클라이언트 코드를 거의 그대로 재사용할 수 있다. 처리량은 모델과 부하 형태에 따라 vLLM보다 앞서기도 뒤지기도 하므로, 아래의 genai-perf 방식으로 직접 재서 판단하면 된다.
1. trtllm-build가 사라진 자리
TensorRT-LLM은 원래 두 단계였다. HuggingFace 체크포인트를 중간 포맷으로 변환하고, trtllm-build로 GPU에 맞춘 TensorRT 엔진을 컴파일한 다음 그 엔진을 서빙했다. 이 빌드가 느리고 옵션이 많아서 진입 장벽이 꽤 높았다.
1.0 GA에서 PyTorch 기반 아키텍처가 기본이자 안정 단계가 됐다. trtllm-serve와 LLM API가 HuggingFace 모델 레퍼런스를 직접 받아서 내부적으로 최적화를 처리한다. 1.2대에 오면 변환과 trtllm-build가 서빙 경로에서 아예 필요 없고 제공되지도 않는다. HF 리포지토리나 TensorRT Model Optimizer 체크포인트를 그냥 가리키면 된다.
백엔드는 PyTorch(기본), TensorRT, AutoDeploy 세 가지가 있는데 --backend 옵션은 이제 사실상 지원 중단이고 PyTorch가 기본이다. 일단 기본값으로 올린다.
2. 설치부터 첫 기동까지
NVIDIA GPU와 최신 드라이버가 있는 리눅스가 전제다. 공식 문서 기준 테스트 환경은 Ubuntu 24.04, Python 3.12, CUDA Toolkit 13.1이다. pip로 올리는 경우 시스템 MPI 라이브러리가 먼저 필요하다.
sudo apt-get -y install libopenmpi-dev pip3 install --ignore-installed pip setuptools wheel pip3 install tensorrt_llm -U --extra-index-url https://pypi.nvidia.com
CUDA 버전을 맞추려고 PyTorch를 cu130 휠로 같이 깔아 두는 쪽이 사고가 적다. 버전 번호는 저장소 상태에 따라 다를 수 있다.
pip3 install torch==2.10.0 torchvision --index-url https://download.pytorch.org/whl/cu130
로컬에 CUDA 13.1 툴킷 맞추기가 번거로우면 NGC 컨테이너로 가는 편이 빠르다. 다만 PyTorch NGC 컨테이너 안에서 미리 빌드된 휠을 넣을 때는 /etc/pip/constraint.txt를 먼저 비워야 설치가 막히지 않는다. 여기서 한 번 걸렸는데, constraint 파일 때문에 버전이 고정돼서 설치가 조용히 실패한다.
설치가 끝나면 모델 이름만 주고 바로 띄운다. 작은 모델로 경로부터 확인한다.
trtllm-serve TinyLlama/TinyLlama-1.1B-Chat-v1.0
기본값은 localhost:8000, PyTorch 백엔드다. 모델을 내려받고 KV 캐시를 잡은 뒤 서버가 올라오면 아래 엔드포인트가 열린다. /v1/models, /v1/completions, /v1/chat/completions로 OpenAI API 구조를 그대로 흉내 낸다.
GPU 메모리가 부족해서 OOM이 나면 KV 캐시가 잡는 비율을 낮춘다. 기본이 꽤 공격적이라 다른 프로세스와 GPU를 나눠 쓸 때 자주 터진다.
trtllm-serve TinyLlama/TinyLlama-1.1B-Chat-v1.0 \ --host 0.0.0.0 --port 8000 \ --max_batch_size 64 \ --max_num_tokens 8192 \ --kv_cache_free_gpu_memory_fraction 0.8
멀티 GPU면 --tp_size(텐서 병렬)와 --pp_size(파이프라인 병렬)로 쪼갠다. 세밀한 설정은 YAML로 빼서 --extra_llm_api_options config.yaml로 넘기면 된다. CLI 플래그에 없는 LLM API 옵션은 여기로 들어간다.
3. OpenAI 클라이언트로 찔러보기
OpenAI 호환이라 기존 vLLM 테스트에 쓰던 curl을 거의 그대로 재사용한다. 모델 이름은 서버에 올린 것과 맞춰야 한다.
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "TinyLlama/TinyLlama-1.1B-Chat-v1.0",
"messages": [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Where is New York?"}
],
"max_tokens": 16,
"temperature": 0
}'
파이썬에서는 openai SDK의 base_url만 바꿔 끼운다. API 키는 아무 문자열이나 받지만 관례적으로 tensorrt_llm을 넣는다.
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="tensorrt_llm")
resp = client.chat.completions.create(
model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
messages=[{"role": "user", "content": "안녕, 한 줄로 자기소개해줘"}],
max_tokens=64,
)
print(resp.choices[0].message.content)
서버 상태는 /metrics 엔드포인트로 본다. GPU 메모리 사용량과 KV 캐시 사용률 같은 런타임 통계가 나온다. 부하를 걸면서 이 값이 포화되는지 보면 배치 크기나 KV 캐시 비율을 어디까지 올릴지 감이 잡힌다.
curl -s http://localhost:8000/metrics | head
4. vLLM과 같은 조건으로 재보기
처리량 비교에서 제일 많이 어긋나는 지점이 "서로 다른 부하로 잰 숫자를 맞붙이는" 것이다. vLLM은 vllm bench serve로, TensorRT-LLM은 다른 도구로 재고 나서 비교하면 입력 길이, 동시성, 스트리밍 여부가 달라 숫자가 의미를 잃는다. 두 서버 다 OpenAI 호환이니 같은 벤치 도구 하나로 양쪽을 똑같이 때리는 게 맞다.
NVIDIA의 genai-perf가 이 역할에 잘 맞는다. OpenAI chat 엔드포인트를 상대로 N개 동시 요청을 유지하면서 재는 멀티프로세스 도구다. 단일 프로세스인 vllm bench serve는 동시성이 높을 때 클라이언트 쪽에서 먼저 포화돼 서버 처리량을 과소평가할 수 있는데, genai-perf는 그 문제가 덜하다.
먼저 trtllm-serve를 8000에 띄워 두고, 합성 입출력 길이와 동시성을 고정해 잰다.
genai-perf profile \ -m TinyLlama/TinyLlama-1.1B-Chat-v1.0 \ --endpoint-type chat \ --url http://localhost:8000 \ --streaming \ --concurrency 32 \ --synthetic-input-tokens-mean 512 \ --output-tokens-mean 128 \ --request-count 500 --warmup-request-count 20
그다음 같은 모델을 vLLM으로 올린다. 포트만 바꾸면 클라이언트 쪽은 동일하다.
vllm serve TinyLlama/TinyLlama-1.1B-Chat-v1.0 --port 8001 genai-perf profile \ -m TinyLlama/TinyLlama-1.1B-Chat-v1.0 \ --endpoint-type chat \ --url http://localhost:8001 \ --streaming \ --concurrency 32 \ --synthetic-input-tokens-mean 512 \ --output-tokens-mean 128 \ --request-count 500 --warmup-request-count 20
genai-perf는 측정마다 Output Token Throughput(초당 출력 토큰), Request Throughput, 사용자당 토큰 처리량 같은 지표를 뽑아 준다. 이 숫자를 양쪽에서 같은 동시성, 같은 입출력 길이로 뽑아야 비교가 성립한다.
어느 쪽이 이기냐는 모델과 부하에 따라 갈린다. 공개 벤치마크들을 보면 긴 컨텍스트에서는 둘이 엎치락뒤치락하는 수준이고, 설정과 모델에 따라 한쪽이 몇 배 앞서는 경우도 보고된다. 그러니 "TensorRT-LLM이 항상 빠르다"는 전제로 가지 말고, 운영에서 실제로 돌릴 모델과 동시성으로 위 두 쌍을 돌려 직접 비교하는 게 맞다. 수치는 GPU, 드라이버, 배치 설정에 따라 크게 달라진다.
한 가지 더 볼 지점은 전력이다. TensorRT-LLM이 하드웨어를 더 꽉 쓰면서 처리량을 뽑는 경향이 있어, 같은 처리량이라도 와트당 성능이나 총 전력에서 차이가 날 수 있다. 처리량만 보고 결정하기 전에 nvidia-smi로 전력과 사용률을 같이 찍어 두면 나중에 비용 계산할 때 쓸모가 있다.
여기까지 왔으면 모델만 운영 후보로 바꿔 끼우면 된다. 큰 모델은 KV 캐시 비율과 --max_num_tokens를 올렸다 내렸다 하면서 OOM 경계를 먼저 찾고, 그 설정으로 genai-perf를 다시 돌려 vLLM 쪽 숫자와 맞붙이면 판단이 선다.