07. Triton + TensorRT: GPU latency 줄이기¶
같은 모델을 더 빠르게 서빙할 수 있을까요?
이 실험에서는 TensorRT FP16 적용 후 p50이 26.8ms에서 15.4ms로 낮아졌습니다. p99도 45.4ms에서 17.4ms로 낮아졌습니다.
01~06은 container와 배포 방식을 비교합니다. 07은 hardware와 model을 고정하고 NVIDIA Triton Inference Server로 실행 engine을 바꿉니다.
실측 결과¶
ml.g5.xlarge(A10G), mDeBERTa ONNX, max_len 512 고정, batch=1 concurrency 1 입니다.
| 구성 | p50 | p90 | p95 | p99 | b=8 samples/s | 클라이언트 |
|---|---|---|---|---|---|---|
| 01 HF DLC + PyTorch | 26.8 ms | 29.8 ms | 32.7 ms | 45.4 ms | 113/s | 텍스트만 |
| Triton + ORT GPU | 27.3 ms | 28.4 ms | 28.5 ms | 28.6 ms | 57/s | tokenize 필요 |
| Triton + TensorRT FP16 | 15.4 ms | 16.4 ms | 16.8 ms | 17.4 ms | 185/s | tokenize 필요 |
| Triton ensemble + TensorRT | 16.6 ms | 17.1 ms | 17.4 ms | 17.9 ms | 179/s | 텍스트만 |
검증한 NLI sample 4건의 판정은 네 구성에서 모두 같았습니다.
Container와 engine의 영향 분리¶
Container와 engine의 영향을 나누어 보기 위해 ORT GPU baseline을 추가했습니다.
| 바뀐 것 | p50 변화 | p99 변화 |
|---|---|---|
| 컨테이너만 (HF DLC → Triton, 엔진은 그대로) | 26.8 → 27.3 ms (변화 없음) | 45.4 → 28.6 ms |
| 엔진 (ORT GPU → TensorRT FP16) | 27.3 → 15.4 ms (1.8배) | 28.6 → 17.4 ms |
container만 교체한 구성에서는 p50이 개선되지 않았고 p99가 45.4ms에서 28.6ms로 낮아졌습니다.
TensorRT를 적용한 뒤 p50은 27.3ms에서 15.4ms로 낮아졌습니다. layer fusion이나 FP16 연산의 개별 기여도는 profiling하지 않았습니다.
batch=8에서 ORT GPU throughput이 낮았습니다
ORT GPU는 57 samples/s, HF DLC는 113 samples/s였습니다. 원인을 분리하려면
dynamic_batching queue와 backend execution을 추가로 profiling해야 합니다. TensorRT
FP16 구성은 185 samples/s였습니다.
ORT CPU와 TensorRT GPU의 fp16 결과가 다릅니다¶
ONNX Runtime으로 CPU 배포에서 INT8은 검증 sample의 예측이 바뀌었고 fp16은 load에 실패했습니다. GPU TensorRT에서는 같은 sample 4건의 판정이 fp32와 일치했습니다.
| 02 (ORT CPU) | 07 (TensorRT GPU) | |
|---|---|---|
| INT8 | 예측이 모두 neutral로 붕괴 |
시도하지 않음 |
| fp16 | Cast(to=FLOAT) 노드와 충돌해 로드 실패 |
✅ 정확도 4/4 |
이 결과는 DeBERTa 전체가 아니라 이 모델의 ORT CPU dynamic quantization 경로에 한정해야 합니다.
기본 Triton 구성은 tensor를 받습니다¶
기본 구성에서는 client가 tokenization과 후처리를 담당합니다.
| HF DLC (01~04, 06) | Triton 기본 (07) | |
|---|---|---|
| 요청 | {"inputs": {"text": ..., "text_pair": ...}} |
input_ids, attention_mask 배열 |
| 응답 | {"label": "entailment", "score": 0.99} |
raw logits |
| tokenize | 컨테이너 | 클라이언트 |
| softmax, label 매핑 | 컨테이너 | 클라이언트 |
| 설정 파일 | code/inference.py |
config.pbtxt |
common/triton_payload.py가 text를 tensor로 바꾸고 logits를 label과 score로 변환합니다.
Ensemble로 text payload 유지¶
python 백엔드로 전처리와 후처리를 서버에 두고 ensemble로 묶으면 클라이언트가 다시
텍스트만 보냅니다.
Ensemble을 추가했을 때 batch=1 latency는 1.2ms(8%) 증가했고, batch=8 throughput은 6 samples/s(3%) 낮아졌습니다.
Text payload를 유지해야 한다면 ensemble 구성을 사용하세요. 이 실험에서 01보다 p50은 낮고 batch=8 throughput은 높았습니다.
실행¶
02에서 만든 ONNX artifact를 그대로 사용합니다. 기본 bucket에 artifact가 있으면
build_triton_repo.py가 해당 경로를 찾습니다.
# ONNX artifact가 없다면 먼저 (02 의 스크립트, mdeberta 변환 약 23초)
uv run python 02_cpu_cohost_multicontainer/scripts/export_onnx.py --models mdeberta
# 기본 구성 (클라이언트가 tokenize)
uv run python 07_gpu_triton/scripts/build_triton_repo.py --upload
uv run python 07_gpu_triton/deploy.py --dry-run
uv run python 07_gpu_triton/deploy.py
uv run python 07_gpu_triton/invoke.py --endpoint <ep>
uv run python -m benchmark.run --mode cloud --endpoint <ep> \
--engine triton --pad-to-max --warmup 20
# TensorRT 없는 baseline (엔진 효과를 분리해 보려면)
uv run python 07_gpu_triton/scripts/build_triton_repo.py --no-tensorrt --upload
# ensemble (서버가 tokenize). 별도 endpoint로 띄워 나란히 비교
uv run python 07_gpu_triton/scripts/build_ensemble.py --upload
uv run python 07_gpu_triton/deploy.py \
--model-data s3://<bucket>/encoder-serving/triton/ensemble-model.tar.gz \
--triton-model ensemble_mdeberta --suffix ens
uv run python 07_gpu_triton/invoke.py --endpoint <ens-ep> --ensemble
uv run python -m common.cleanup --delete-all # 실습 리소스 삭제
놓치기 쉬운 것들¶
Triton image와 inference AMI 호환성¶
실습에 사용한 Triton image는 기본 inference AMI의 NVIDIA driver와 호환되지 않았습니다.
ProductionVariant.InferenceAmiVersion을 지정해 해결했습니다.
정상 시작 log에서 CUDA Forward Compatibility mode를 확인할 수 있습니다.
NOTE: CUDA Forward Compatibility mode ENABLED.
Using CUDA 13.2 driver version 595.58.03 with kernel driver version 580.173.02.
자세한 값은 inference AMI 버전 문서에 있습니다.
TensorRT는 batch shape별로 engine을 compile합니다¶
처음 사용하는 shape에서는 engine compile 시간이 첫 호출에 포함됩니다.
| 호출 | 실측 |
|---|---|
| 첫 단건 호출 (b=1) | 47.7초 |
| 이후 단건 | 15~17 ms |
| 첫 배치 호출 (b=8) | 60초 초과로 타임아웃 |
| 이후 배치 | 40~45 ms |
batch=8 첫 호출은 SageMaker container 응답 제한을 넘어 ModelError가 발생했습니다.
Container log에서는 compile 완료를 확인했고 이후 호출은 정상적으로 처리됐습니다.
운영 전 예상 batch shape을 warmup하거나 trtexec로 미리 compile한 .plan을
tensorrt backend에 배포하는 방식을 검토하세요. .plan은 GPU architecture에 종속됩니다.
trt_engine_cache_enable을 사용해도 cache 경로가 /tmp이므로 container를 다시 시작하면
Cache가 사라집니다. 05 scale to zero와 함께 사용할 때는 복구 후
Compile 시간을 cold start에 포함해 측정하세요.
config.pbtxt는 protobuf text format 입니다¶
배열 원소를 쉼표로 구분해야 합니다. 개행만 넣으면 이렇게 실패합니다.
Error parsing text-format inference.ModelConfig: 11:3: Expected ",", found "{".
error: creating server: Internal - failed to load all models
다음으로 읽을 것¶
- Triton이 무엇이고 백엔드가 어떻게 다른가 → Triton Inference Server
- CPU 경로와 비교 → ONNX Runtime으로 CPU 배포
- vLLM은 왜 안 되는가 → 인코더는 왜 다른가
막힌 경우 → 트러블슈팅