- Published on
Triton GPU 100%인데 느린 이유와 진단법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서빙에서 가장 당황스러운 상황 중 하나가 nvidia-smi 상 GPU Util이 100%인데도 QPS가 낮고 p95 지연이 높은 경우입니다. 결론부터 말하면 GPU 100%는 “연산이 꽉 찼다”가 아니라 “GPU에 뭔가 일이 계속 있다”에 가깝습니다. 메모리 복사, 작은 커널이 난사되는 상황, 동기화 대기, 디코더나 전처리로 인한 파이프라인 불균형도 100%로 보일 수 있습니다.
이 글은 Triton Inference Server 기준으로, GPU 100%인데 느린 현상을 병목 관점에서 분해하고 재현 가능한 진단 순서와 튜닝 포인트를 정리합니다. 운영 중 무중단으로 설정을 바꿔가며 검증해야 한다면 Triton Inference Server 모델 핫스왑 배포·롤백 실전도 함께 참고하면 좋습니다.
1) GPU Util 100%의 함정: 무엇이 100%인가
nvidia-smi의 GPU Util은 SM 점유율만을 정확히 의미하지 않습니다. 드라이버 관점에서 “GPU가 바쁘다”의 비율에 가까워서 아래 상황에서도 높게 나옵니다.
- 작은 커널이 매우 자주 실행되어 런치 오버헤드가 큰 경우
- Host to Device 복사가 잦아 PCIe 전송이 바쁜 경우
- 동기화가 잦은 그래프로 인해 커널 사이 공백이 생기지만 드라이버가 바쁘게 보이는 경우
- 메모리 대역폭 포화로 SM이 놀아도 전체 Util이 높게 보이는 경우
따라서 GPU Util만 보지 말고 아래를 같이 봐야 합니다.
nvidia-smi dmon의sm,mem,enc,dec,pcie지표- Nsight Systems에서 커널 타임라인과 H2D/D2H 복사
- Triton metrics에서 queueing과 compute 구간 분해
2) 먼저 확인할 것: 느린 것은 TPS인가, 지연인가
같은 “느림”이라도 원인이 다릅니다.
- TPS가 낮다: GPU가 충분히 큰 배치를 못 먹거나, 동시성이 부족하거나, 전송/전처리 병목
- p95 지연이 높다: 큐잉이 길거나, 동적 배치 대기, 스케줄링 지연, 콜드 캐시, CPU 병목
Triton은 Prometheus metrics로 구간을 나눠 볼 수 있습니다. 대표적으로 아래 3가지를 합쳐 end-to-end가 됩니다.
nv_inference_queue_duration_usnv_inference_compute_input_duration_usnv_inference_compute_infer_duration_usnv_inference_compute_output_duration_us
큐 시간만 커지면 동시성 설정이나 배치 정책이 의심되고, input/output 시간이 크면 전송이나 후처리, infer만 크면 커널 효율이나 정밀도, 엔진 설정이 의심됩니다.
3) 가장 흔한 원인 1: 동시성 부족으로 작은 배치만 계속 도는 경우
GPU가 100%인데도 느린 대표 케이스는 “GPU는 바쁜데, 하는 일이 너무 잘게 쪼개져 있다”입니다. 요청이 들어올 때마다 batch 1로 실행되거나, 인스턴스 수가 부족해 커널이 연속적으로 쏟아지면 Util은 높지만 처리량은 낮습니다.
점검 포인트
- 모델이
max_batch_size를 지원하는데도 실제 배치가 1로만 도는가 dynamic_batching이 꺼져 있거나,max_queue_delay_microseconds가 너무 작아 배치가 못 모이는가instance_group에서 GPU 인스턴스가 1개로 고정되어 있는가
해결: 동적 배치와 인스턴스 그룹 조정
아래는 Triton config.pbtxt 예시입니다.
name: "my_model"
platform: "tensorrt_plan"
max_batch_size: 32
instance_group [
{
kind: KIND_GPU
count: 2
gpus: [0]
}
]
dynamic_batching {
preferred_batch_size: [4, 8, 16, 32]
max_queue_delay_microseconds: 2000
}
count를 늘리면 동시 실행이 늘어 지연이 줄 수 있지만, 메모리 사용량과 컨텍스트 오버헤드가 증가합니다.max_queue_delay_microseconds는 지연과 처리량의 트레이드오프입니다. p95를 보며 단계적으로 늘리세요.
4) 가장 흔한 원인 2: 입력 전처리 또는 후처리가 CPU 병목인데 GPU는 계속 대기/짧은 작업 반복
클라이언트에서 이미지 디코딩, 리사이즈, 토크나이즈를 하고 Triton으로 보내면 CPU가 병목이 될 수 있습니다. 이때 GPU는 batch 1의 짧은 infer를 계속 반복하며 100%처럼 보이지만, 실제로는 요청이 충분히 공급되지 않아 TPS가 낮습니다.
점검 포인트
- 클라이언트 프로세스 CPU 100% 여부
- Triton server의
nv_inference_queue_duration_us가 작고 요청 간 간격이 큰지 - 전처리를 Python backend로 하고 있다면 GIL 영향과 단일 스레드 여부
해결 방향
- 전처리 자체를 GPU로 옮기거나, DALI 같은 파이프라인을 고려
- Python backend라면 멀티 프로세스 분리 또는 C++/TensorRT로 이전
- 클라이언트는 비동기/멀티스레드로 inflight를 늘려 GPU에 충분히 공급
간단한 부하 생성은 perf_analyzer로 대체해 “서버 자체 처리량”을 먼저 확인하는 게 좋습니다.
perf_analyzer -m my_model \
--concurrency-range 1:64:4 \
--measurement-interval 5000 \
--percentile 95 \
--input-data zero
perf_analyzer로는 빠른데 실제 서비스가 느리면, 모델 밖(전처리/네트워크/클라이언트) 병목일 확률이 큽니다.
5) 가장 흔한 원인 3: Host to Device 복사와 메모리 핀ning 문제
GPU Util이 높은데도 지연이 늘어지는 케이스에서 pcie가 높게 나오면 H2D/D2H 복사가 병목일 수 있습니다. 특히 입력 텐서가 크고 배치가 작으면 “복사 오버헤드 / 연산” 비율이 나빠집니다.
점검 포인트
nvidia-smi dmon에서 PCIe Tx/Rx가 높은지- Nsight Systems에서 H2D memcpy가 infer 앞뒤로 길게 붙는지
해결 방향
- 가능하면 큰 배치로 복사 amortize
- 클라이언트에서 pinned memory 사용 여부 확인
- gRPC/HTTP payload가 크다면 압축이나 전송 형식 최적화
- 입력을 FP32에서 FP16으로 줄일 수 있는지(모델 허용 시)
6) 가장 흔한 원인 4: 메모리 대역폭 포화 또는 비효율 커널
딥러닝 모델이 항상 연산 집약적이지는 않습니다. 레이어 구성에 따라 메모리 바운드가 되면 SM이 덜 바빠도 전체 Util이 높게 보일 수 있고, 처리량은 기대보다 낮습니다.
점검 포인트
nvidia-smi dmon에서mem이 높고sm이 상대적으로 낮은지- TensorRT 엔진에서 FP16/INT8 최적화가 적용되었는지
- attention 계열에서 sequence 길이가 길어 KV cache 접근이 병목인지
해결 방향
- TensorRT로 변환 시 FP16 또는 INT8 캘리브레이션 적용
- 불필요한 출력 제거, 레이어 퓨전 유도
- LLM 계열이면 paged attention, KV cache 최적화, batching 전략 재설계
7) 가장 흔한 원인 5: 동적 배치가 오히려 지연을 키우는 경우
동적 배치는 처리량을 올리지만, 트래픽 패턴이 불규칙하거나 목표가 low-latency라면 큐 대기가 p95를 악화시킬 수 있습니다. 이때 GPU는 배치를 기다리며 바쁘게 보이거나, 배치가 모일 때만 크게 돌면서 Util이 출렁입니다.
해결: 정책을 지연 목표에 맞추기
preferred_batch_size를 줄이고max_queue_delay_microseconds를 낮춰 p95를 맞추기- 트래픽이 낮을 때는 batch 1로도 빠르게 처리하도록 폴백 설계
- 모델을 2개 엔드포인트로 분리해 low-latency용과 high-throughput용을 운영
8) 가장 흔한 원인 6: 모델 인스턴스 수를 늘렸더니 오히려 느려짐
인스턴스를 늘리면 동시성이 늘지만, 다음 문제가 생길 수 있습니다.
- GPU 메모리 압박으로 paging 또는 allocator overhead 증가
- L2 cache thrash, 컨텍스트 스위칭 증가
- TensorRT 엔진이 multi-stream에서 최적이 아닐 수 있음
튜닝 요령
- 인스턴스 수는
1부터 단계적으로 올리며QPS,p95,GPU mem,sm을 같이 관찰 - concurrency는 인스턴스 수만으로 해결하지 말고 동적 배치와 함께 조합
9) 가장 흔한 원인 7: Python backend 또는 ensemble에서 동기화 지옥
Triton ensemble로 전처리 python + infer + 후처리를 엮으면, 단계 사이 텐서 이동과 동기화가 늘고 병목이 숨습니다. GPU는 100%처럼 보이지만 실제로는 python 단계가 전체를 조이고 있을 수 있습니다.
점검 포인트
- 단계별 latency를 분리해서 측정했는지
- python 단계가 단일 인스턴스로 동작하는지
해결 방향
- python 단계는 최소화하거나 C++ backend로 이전
- 전처리/후처리를 애초에 클라이언트로 이동하고 서버는 pure infer로 유지
- ensemble을 유지해야 한다면 각 단계 인스턴스 수와 배치 정책을 따로 튜닝
10) 실전 진단 순서: 30분 안에 병목 찾기
아래 순서로 보면 “GPU 100%인데 느림”의 원인을 빠르게 좁힐 수 있습니다.
1단계: 서버 단독 처리량 확인
- 실제 트래픽 대신
perf_analyzer로 모델 최대 처리량과 최적 concurrency 찾기 - 이 결과가 기대치보다 낮으면 모델 엔진 또는 배치/인스턴스 설정 문제
2단계: Triton metrics로 큐 vs compute 분해
- 큐가 길면 동시성/배치/스케줄링
- input/output이 길면 전송/전처리/후처리
- infer가 길면 엔진/정밀도/커널 효율
3단계: nvidia-smi dmon으로 sm, mem, pcie 확인
pcie높음: 복사 병목mem높음: 메모리 바운드sm높음인데 QPS 낮음: 작은 커널 난사, 동시성/배치 재검토
4단계: Nsight Systems로 타임라인 확인
- H2D memcpy가 길게 붙는지
- 커널 사이 공백이 많은지
- stream이 충분히 활용되는지
11) 자주 쓰는 설정/명령 모음
Triton 실행 시 metrics 노출
docker run --gpus all --rm -p 8000:8000 -p 8001:8001 -p 8002:8002 \
nvcr.io/nvidia/tritonserver:24.XX-py3 \
tritonserver --model-repository=/models --metrics-port=8002
Prometheus에서 볼 핵심 지표(이름 예시)
nv_inference_request_successnv_inference_queue_duration_usnv_inference_compute_infer_duration_usnv_inference_compute_input_duration_usnv_inference_compute_output_duration_us
환경에 따라 label이 많아 쿼리가 길어질 수 있으니, 모델명과 버전 label을 먼저 고정하고 보세요.
12) 결론: GPU 100%는 원인이 아니라 증상이다
Triton에서 GPU가 100%인데 느릴 때는 “GPU가 일을 너무 많이 해서”가 아니라, 대부분 아래 중 하나입니다.
- 배치/동시성이 맞지 않아 작은 작업을 과도하게 반복
- 전처리/후처리/클라이언트가 병목이라 GPU에 공급이 부족
- H2D/D2H 복사와 PCIe가 병목이라 전송이 지연을 잠식
- 메모리 바운드/비효율 커널로 연산 대비 효율이 낮음
- 동적 배치가 트래픽 패턴과 맞지 않아 큐 대기가 p95를 악화
운영 환경에서는 설정 변경이 리스크이므로, 모델을 바꾸지 않고도 설정을 점진적으로 조정하며 검증하는 흐름이 중요합니다. 무중단으로 모델과 설정을 교체하며 실험해야 한다면 Triton Inference Server 모델 핫스왑 배포·롤백 실전을 기반으로 안전한 롤백 루틴을 먼저 갖춰두는 것을 권장합니다.