제5부 추론 최적화와 서빙
5.3 TensorRT II — 빌더 최적화·동적 shape
5.3.1 이 장에서 배우는 것 / 선수 지식
5.2에서 ONNX→엔진 빌드의 큰 흐름을 파악하고, optimization profile(min/opt/max)을 맛만 봤다. 이 장은 그 동적 shape를 전면으로 끌어와, 하나의 엔진으로 가변 배치·가변 해상도·가변 시퀀스를 서빙하는 법과, 빌더 최적화 손잡이(optimization level·tactic source·timing cache·builder flag)를 실습으로 깊게 판다.
왜 동적 shape가 추론의 핵심인가? 5.1에서 봤듯 추론 부하는 내가 통제하지 못한다 — 사용자가 배치 1을 보낼 수도, 32를 보낼 수도, 이미지가 224²일 수도 640²일 수도 있다. 모든 형상마다 엔진을 따로 빌드·적재하면 메모리·운영이 폭발한다. 동적 shape는 형상의 범위(min~max)를 하나의 엔진에 담아, 런타임에 실제 형상을 골라 실행한다. 그 대가는 "범위가 넓을수록 각 형상의 최적화가 무뎌진다"는 것 — 이 균형을 optimization profile로 설정하는 것이 이 장의 본질이다.
배우는 것:
- 정적 vs 동적 shape, explicit batch, 그리고 동적 차원(
-1)이 빌드·실행에 미치는 영향. - optimization profile(min/opt/max)의 의미 — 빌더가 opt 형상에 맞춰 tactic을 고르고, min~max 범위에서 동작을 보장하는 구조.
- 다중 프로파일 — 형상 분포가 갈릴 때 여러 프로파일을 두고 런타임에 선택.
- shape tensor vs execution tensor(형상 자체가 입력이 되는 경우)와 그 함정.
- 빌더 손잡이 — optimization level·tactic source·timing cache·builder flag(TF32·FP16·sparse·refit 등) — 의 효과.
- 프로파일을 실제 입력 분포에 맞추는 튜닝과,
trtexec/polygraphy로 동적 엔진을 검증·프로파일하기(실습).
선수 지식: 5.2(빌드 흐름·trtexec·Python API·polygraphy — 이 장은 그 위에 쌓는다), 5.1(배치=마스터 노브), 2.3(실행 구성·형상이 성능에 미치는 영향), 1.13~1.14(정밀도 — builder flag). 환경은 5.2과 동일(NGC TensorRT 컨테이너 권장).
정직: 동적 shape API(
set_shape·set_input_shape·프로파일 선택 메서드)와 builder flag 이름은 TRT 버전 의존이다(메서드 개명·deprecation 잦음). 흐름은 버전 불변이나 정확한 시그니처는 당신 버전 문서로 확인("시점 의존, 확인 필요"). 성능 수치는 모두 환경 의존 개략값 — 실측.
5.3.2 정적 vs 동적 shape — 무엇이·왜
| 구분 | 정적 shape 엔진 | 동적 shape 엔진 |
|---|---|---|
| 입력 형상 | 고정 | 차원 일부가 동적(-1) |
| 예 | input=8x3x224x224 | input=(-1)x3x(-1)x(-1) |
| 동작 범위 | 그 형상에만 최적·동작 | min~max 범위에서 동작 |
| 여러 배치 | 다른 배치 → 다른 엔진 | 하나의 엔진이 여러 형상 처리 |
| 장 | 형상이 고정되어 최적화 최대 | 가변 부하를 하나로 서빙(운영↓) |
| 단 | 형상마다 엔진 폭발 | 범위가 넓을수록 형상별 최적화 무뎌짐 |
explicit batch(명시적 배치). ONNX 경로는 항상 배치 차원을 그래프에 명시한다(5.2의 EXPLICIT_BATCH). 즉 배치도 다른 차원과 똑같이 동적/정적으로 다룰 수 있는 일반 차원이다. (과거 implicit batch는 deprecated — 신경 쓰지 말 것.)
동적 차원의 표기. ONNX export에서 dynamic_axes로 지정한 축이 엔진에서 동적(-1)이 된다(5.2.4). 흔히 동적으로 두는 축:
- 배치(N) — 거의 항상. 가변 동시 요청·동적 배칭(5.13)의 전제.
- 시퀀스 길이(L) — NLP·LLM(가변 입력 길이). (LLM은 TRT-LLM이 따로, 5.7.)
- 해상도(H·W) — 검출·세그멘테이션(다양한 입력 크기).
핵심 트레이드오프: 동적 범위는 무비용이 아니다. 빌더는 범위 전체에서 정확히 동작하는 커널을 골라야 하므로, 너무 넓은 min~max는 특정 형상의 최고 성능을 저하시킬 수 있다. 실제로 들어올 형상의 분포에 범위를 맞추는 것이 성능의 핵심 요인(5.3.5).
5.3.3 optimization profile — min/opt/max의 작동
동적 엔진을 빌드하려면 각 동적 입력에 optimization profile을 줘야 한다. 세 형상을 지정한다:
profile.set_shape("input",
min = (1, 3, 224, 224), # 허용 최소 — 이보다 작으면 실행 불가
opt = (8, 3, 224, 224), # 빌더가 이 형상에 맞춰 tactic을 고름(가장 빠르게 됨)
max = (32, 3, 224, 224)) # 허용 최대 — 이보다 크면 실행 불가
세 형상의 역할:
| 형상 | 의미 | 잘못 잡으면 |
|---|---|---|
| min | 런타임 입력의 하한 | 더 작은 형상은 실행 거부(런타임 에러) |
| opt | 빌더가 오토튜닝(tactic 선택) 기준으로 삼는 형상 | 실제 흔한 형상과 다르면 그 형상에서 비최적 |
| max | 런타임 입력의 상한 + 메모리 할당 기준 | 너무 크면 활성화 메모리 과할당, 너무 작으면 큰 입력 거부 |
메모리. TensorRT는 보통 max 형상 기준으로 활성화 메모리를 할당한다(런타임에 형상이 바뀌어도 재할당 없이 동작하도록). 그래서 max를 실제 필요보다 크게 잡으면 메모리 낭비다 — 동시 요청 수(5.1.7)에 직접 영향.
5.3.4 워크스루 (1) — 동적 엔진 빌드·실행
5.2의 ResNet-50을 배치 동적으로 빌드해 여러 배치로 실행한다.
5.3.4.1 trtexec로 동적 엔진 빌드
# min/opt/max를 주면 동적 배치 엔진. opt를 '가장 흔한 배치'로.
trtexec \
--onnx=resnet50.onnx \
--saveEngine=resnet50_dyn.plan \
--fp16 \
--minShapes=input:1x3x224x224 \
--optShapes=input:8x3x224x224 \
--maxShapes=input:32x3x224x224 \
--memPoolSize=workspace:4096
# 특정 형상으로 벤치마크(런타임 형상 지정)
trtexec --loadEngine=resnet50_dyn.plan --shapes=input:1x3x224x224 # 배치 1
trtexec --loadEngine=resnet50_dyn.plan --shapes=input:8x3x224x224 # 배치 8(opt)
trtexec --loadEngine=resnet50_dyn.plan --shapes=input:32x3x224x224 # 배치 32(max)
관찰(개략): 배치를 1→8→32로 늘리면 Throughput(qps·img/s)은 오르고(메모리 바운드 완화, 5.1.4) 요청당 지연은 늘어난다. opt(8)에서 효율이 좋고, opt에서 먼 형상(특히 1)은 상대적으로 비효율일 수 있다 — 이것이 "opt를 실제 흔한 배치에 맞추라"는 이유.
5.3.4.2 Python API — 동적 빌드(프로파일) + 가변 실행
빌드는 5.2.5의 build_engine에 프로파일이 이미 들어 있다(거기서 set_shape 했다). 추론에서 매 실행마다 형상을 지정하는 것이 동적 엔진의 핵심:
# infer_dyn.py — 같은 엔진을 여러 배치로 실행
import numpy as np, tensorrt as trt
from cuda import cudart # 패키지·API 버전 확인
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
def load_engine(p):
rt = trt.Runtime(TRT_LOGGER)
with open(p, "rb") as f:
return rt.deserialize_cuda_engine(f.read())
def run(engine, ctx, x): # x: (N,3,224,224) float32
ctx.set_input_shape("input", x.shape) # ★ 이번 실행의 실제 형상(min~max 안)
assert ctx.all_binding_shapes_specified
out_shape = tuple(ctx.get_tensor_shape("logits"))
out = np.empty(out_shape, np.float32)
_, d_in = cudart.cudaMalloc(x.nbytes)
_, d_out = cudart.cudaMalloc(out.nbytes)
cudart.cudaMemcpy(d_in, x.ctypes.data, x.nbytes,
cudart.cudaMemcpyKind.cudaMemcpyHostToDevice)
ctx.set_tensor_address("input", int(d_in))
ctx.set_tensor_address("logits", int(d_out))
_, s = cudart.cudaStreamCreate()
ctx.execute_async_v3(s); cudart.cudaStreamSynchronize(s)
cudart.cudaMemcpy(out.ctypes.data, d_out, out.nbytes,
cudart.cudaMemcpyKind.cudaMemcpyDeviceToHost)
cudart.cudaFree(d_in); cudart.cudaFree(d_out); cudart.cudaStreamDestroy(s)
return out
if __name__ == "__main__":
engine = load_engine("resnet50_dyn.plan")
ctx = engine.create_execution_context() # context는 재사용(매번 새로 만들지 말 것)
for n in (1, 8, 32): # 하나의 엔진·하나의 context로 여러 배치
y = run(engine, ctx, np.random.randn(n,3,224,224).astype(np.float32))
print(f"batch {n:>2}: out={y.shape}")
# 범위 밖이면 실패: run(engine, ctx, np.zeros((64,3,224,224),np.float32)) → 에러(>max)
python infer_dyn.py
# batch 1: out=(1, 1000)
# batch 8: out=(8, 1000)
# batch 32: out=(32, 1000)
핵심 습관: execution context와 GPU 버퍼는 재사용한다(매 요청 새로 만들면 오버헤드). 실무 서빙은 max 형상 기준 버퍼를 한 번 할당하고, 형상만 바꿔 실행 — 그 운영을 대신 해주는 것이 Triton(5.11).
5.3.5 워크스루 (2) — 프로파일을 실제 분포에 맞추기
opt 형상을 실제로 가장 흔한 형상에 맞추는 것이 동적 엔진 튜닝의 핵심이다. 워크스루:
수치 규모(개략): 같은 동적 엔진이라도 opt=8로 빌드한 엔진은 배치 8 근처에서 가장 빠르고, opt=1로 빌드하면 배치 1에서 더 낫지만 큰 배치에서 손해를 볼 수 있다. "하나의 opt로 모든 형상 최적"은 불가 — 분포에 맞춘 opt + 필요 시 다중 프로파일이 답이다.
5.3.6 다중 optimization profile
형상 분포가 한 봉우리가 아니면(저지연 배치 1 + 고처리량 배치 32가 둘 다 잦음), 여러 프로파일을 한 엔진에 넣고 런타임에 고른다.
# 빌드: 프로파일 2개 — 하나는 작은 배치 최적, 하나는 큰 배치 최적
p_small = builder.create_optimization_profile()
p_small.set_shape("input", (1,3,224,224), (1,3,224,224), (4,3,224,224))
config.add_optimization_profile(p_small) # profile index 0
p_large = builder.create_optimization_profile()
p_large.set_shape("input", (8,3,224,224), (16,3,224,224), (32,3,224,224))
config.add_optimization_profile(p_large) # profile index 1
# build_serialized_network(...) → 두 프로파일을 가진 하나의 엔진
# 런타임: 형상에 맞는 프로파일을 선택(메서드명·방식은 버전 의존)
ctx.set_optimization_profile_async(0, stream) # 작은 배치 요청엔 profile 0
ctx.set_input_shape("input", (1,3,224,224))
# ... 큰 배치 요청엔 profile 1로 전환(별도 context를 두는 패턴이 흔함)
트레이드오프:
- 장: 각 형상대(帶)를 그 대에 최적화된 tactic으로 → 분포가 갈려도 전 구간 효율.
- 단: 프로파일마다 메모리·엔진 크기 증가, 프로파일 전환 비용, 운영 복잡↑. 프로파일을 무한정 늘리지 말 것.
- 실무 패턴: 프로파일당 별도 execution context를 두어 동시에 여러 형상대를 처리(컨텍스트는 프로파일에 묶임). 이 다중 컨텍스트·라우팅도 Triton(5.12 인스턴스·동시성)이 잘 다룬다.
5.3.7 shape tensor vs execution tensor (함정 주의)
대부분의 텐서는 execution tensor(데이터가 흐르는 보통 텐서)다. 그러나 일부 연산은 형상 값 자체를 입력으로 받는다(예: Reshape의 목표 형상, Resize의 크기) — 이를 shape tensor라 한다.
| 텐서 종류 | 내용 | 결정 위치 |
|---|---|---|
| execution tensor | 픽셀·특징 같은 '데이터' | GPU에서 계산됨 |
| shape tensor | 형상·인덱스 같은 '메타 정수' | 종종 CPU/호스트에서 결정 |
shape tensor가 동적이면(런타임에 정해지면) 프로파일에서 그 값의 min/opt/max도 따로 지정해야 하고, 런타임에 set_shape_input(또는 버전별 대응 API)로 값을 넣어줘야 한다. 이를 빠뜨리면 빌드/실행 실패나 미정의 동작이 난다.
함정: "Reshape/Resize가 동적인데 왜 빌드가 안 되지?"의 흔한 원인이 shape tensor 프로파일 누락이다. ONNX export 시 형상이 상수로 굳어지면(정적) 문제없지만, 형상이 입력에 의존하면 shape tensor 처리가 필요하다. polygraphy
inspect model로 어떤 텐서가 shape tensor인지 확인.
5.3.8 빌더 손잡이 — 깊게
5.2에서 목록만 본 손잡이를 동작과 함께.
| 손잡이 | 동작 | 언제 |
|---|---|---|
| optimization level | 오토튜닝 탐색 강도(낮음~높음) | 개발=낮게(빠른 빌드), 배포=높게(빠른 엔진). 5.2.11 연습5 |
| timing cache | 레이어별 tactic 측정 결과 캐시 | 같은 GPU·같은 TRT면 재빌드 대폭 단축. 파일로 저장·재사용 |
| tactic sources | 어떤 커널 라이브러리(cuBLAS·cuDNN·edge 등)를 탐색 후보로 | 특정 소스 끄면 빌드 빨라지나 최적 tactic 놓칠 수도(버전 의존) |
| builder flags | FP16·INT8(5.4)·TF32·SPARSE_WEIGHTS(2:4, 1.14)·REFIT 등 | 정밀도·희소성·refit 가능 엔진 등 빌드 특성 결정 |
| REFIT | 가중치를 재빌드 없이 교체 가능한 엔진 | 같은 구조에 가중치만 바뀔 때(파인튜닝 반복) 재빌드 절약. 약간의 성능 대가 가능 |
| preview features | 신기능 미리보기 플래그(버전별) | 최신 최적화 실험. 변동 가능, 문서 확인 |
timing cache 실습 요령:
# 첫 빌드: 캐시 생성 / 이후 빌드: 캐시 재사용으로 오토튜닝 측정 생략
trtexec --onnx=resnet50.onnx --saveEngine=a.plan --fp16 \
--timingCacheFile=trt.cache # 없으면 만들고, 있으면 읽어 가속
# 같은 GPU·TRT에서 두 번째 빌드부터 빌드 시간이 눈에 띄게 줄어듦(개략)
정직: flag·preview feature 이름과 가용성은 버전마다 다르다.
SPARSE_WEIGHTS(구조적 희소, 1.14)는 가중치가 2:4 패턴이고 하드웨어가 지원해야 실효가 있다 — 켠다고 자동으로 빨라지지 않는다. 항상 그 버전 문서 + 실측.
5.3.9 중간 점검 (체크포인트)
- [ ] 정적 vs 동적 shape의 트레이드오프를 대고, 어떤 축(배치·시퀀스·해상도)을 왜 동적으로 두는지 설명한다.
- [ ] optimization profile의 min/opt/max 역할을 각각 말하고, opt가 성능에, max가 메모리에 미치는 영향을 안다.
- [ ] 하나의 동적 엔진을 여러 형상으로 실행하고(Python/trtexec), 범위 밖 입력이 거부됨을 확인한다.
- [ ] 입력 분포에 맞춰 opt를 정하고, 분포가 갈리면 다중 프로파일을 쓸 줄 안다.
- [ ] shape tensor와 execution tensor를 구분하고, 동적 형상 입력의 프로파일 누락 함정을 안다.
- [ ] timing cache·optimization level로 빌드 시간을, builder flag로 엔진 특성을 조절한다.
증상→의심: 배치 1은 빠른데 큰 배치가 비효율 → opt가 작은 배치에 맞춰짐 → opt 조정·다중 프로파일. 활성화 메모리가 과도, 동시 요청 수↓ → max를 너무 크게 잡음 → 실제 상한으로 축소. 런타임에 "shape out of range" 에러 → 입력이 min~max 밖 → 범위 확대 또는 입력 클램프. Reshape/Resize 동적 모델 빌드 실패 → shape tensor 프로파일 누락. 재빌드가 매번 느림 → timing cache 미사용.
5.3.10 트레이드오프·대안
동적 범위를 얼마나 넓게? 핵심 결정.
| 선택 | 누구에게 | 비고 |
|---|---|---|
| 좁은 동적 범위 + opt 정조준 | 형상 분포가 한 봉우리 | 단순·고성능. 범위 밖 입력은 거부(클램프·패딩 필요) |
| 넓은 동적 범위(1개 프로파일) | 형상이 매우 다양·예측 어려움 | 운영 단순하나 형상별 최적화 무뎌짐·메모리(max)↑ |
| 다중 프로파일 | 분포가 둘 이상 봉우리(저지연+고처리량) | 전 구간 효율, 운영·메모리 복잡↑ |
| 형상대별 정적 엔진 여러 개 | 형상 종류가 소수로 고정 | 각 형상 최대 성능, 엔진·적재 관리 부담 |
견해: "실제 분포에 범위를 정확히 맞추고 opt를 트래픽 봉우리에 두는" 게 대개 최선이다. 막연히
1~256같은 넓은 범위를 잡으면 메모리·성능을 둘 다 손해 본다. 형상이 정말 소수(예: 224²와 640² 둘뿐)면 정적 엔진 2개가 더 빠를 수도 있다 — 측정으로 결정. 가변 시퀀스 LLM은 이 일반 TRT 동적 shape가 아니라 TRT-LLM의 인플라이트 배칭·페이지드 KV(5.7~5.8)가 더 적합하다.
비엔비디아 정직: ONNX Runtime 등도 동적 shape를 지원하지만, 형상별 오토튜닝의 깊이·최신 정밀도 흡수가 TRT의 차별점이다. 반대로 형상이 극도로 가변적이고 컴파일 비용이 부담이면, 컴파일 없는 런타임이 운영상 단순할 수 있다 — 워크로드로 저울질.
5.3.11 흔한 함정
- opt를 임의로(예: max와 동일). opt는 가장 흔한 형상에 둬야 평균 성능이 산다. max=opt로 두면 작은 배치가 비효율.
- max를 과도하게 크게. 활성화 메모리를 max 기준으로 잡으므로 동시 요청 수·다른 모델 적재를 제한한다. 실제 상한에 맞춰라.
- 범위 밖 입력 미처리. min보다 작거나 max보다 큰 입력은 런타임 거부. 서빙단에서 패딩·클램프·라우팅 정책 필요.
- shape tensor 프로파일 누락. 동적 Reshape/Resize 등은 형상 값의 min/opt/max도 지정·주입해야 한다. 빠뜨리면 빌드/실행 실패.
- context를 매 요청 새로 만든다. execution context·버퍼는 재사용. 매번 생성은 큰 오버헤드(서빙은 Triton에 위임).
- 다중 프로파일을 남발. 프로파일마다 메모리·복잡도 증가. 분포가 실제로 갈릴 때만, 최소 개수로.
- timing cache를 GPU/버전 넘나들며 재사용. 캐시는 같은 GPU·같은 TRT에서만 유효 — 다르면 무효(또는 무시)되니 환경별로 관리.
- 빌더 flag를 켜면 자동 가속이라 착각. SPARSE_WEIGHTS·FP16 등은 하드웨어·가중치 조건이 맞아야 실효. 켜고 끝이 아니라 실측으로 확인.
5.3.12 연습문제
연습 1 (min/opt/max). 배치만 동적인 엔진을 min=1, opt=8, max=32로 빌드했다. (a) 런타임에 배치 4를 넣으면? (b) 배치 48을 넣으면? (c) 배치 1과 8 중 어디가 더 효율적일 가능성이 큰가, 왜? (d) max를 128로 높이면 무엇이 나빠지나?
정답
(a) 정상 실행(1~32 안). 단 opt(8)에서 멀수록 비최적일 수 있어 4는 8보다 약간 덜 최적일 수 있음. (b) 실행 거부(>max=32) — "shape out of range". (c) 배치 8 — 빌더가 opt=8에 맞춰 tactic을 골랐기 때문. 배치 1은 opt에서 멀고 메모리 바운드라 GPU도 덜 채워짐(5.1). (d) max=128이면 활성화 메모리를 128 기준으로 할당 → 메모리 낭비, 동시 요청 수·다른 모델 적재 여력↓. 실제로 48만 들어온다면 max=48이 적절.
연습 2 (분포 맞추기). 입력 배치 분포가 [1:10%, 2:40%, 4:35%, 8:15%]이다. (a) opt를 어디에 둘지와 이유, (b) min/max는? (c) 측정으로 검증하는 절차.
정답
(a) opt를 2 또는 2~4 사이(트래픽 봉우리)에 — 가장 잦은 형상에서 최적이라야 가중 평균 성능이 산다. (단순히 평균 배치가 아니라 빈도 가중으로 본다.) (b) min=1, max=8(실제 하한·상한에 딱). 더 넓게 잡을 이유 없음. (c) 각 배치(1·2·4·8)를 trtexec --shapes로 지연·처리량 측정 → 빈도로 가중한 평균 성능을 계산 → opt를 2·3·4로 바꿔 빌드해 비교 → 가중 평균이 최선인 opt 채택. 봉우리가 둘로 갈리면 다중 프로파일 고려.
연습 3 (다중 프로파일). 한 서비스가 (i) 대화형 저지연(배치 1, p99 중요)과 (ii) 야간 배치 처리(배치 32, 처리량 중요)를 같은 모델로 한다. 단일 프로파일과 다중 프로파일 중 무엇이 맞고, 어떻게 구성·운영하나?
정답
다중 프로파일이 맞다 — 두 봉우리(1과 32)가 성격까지 달라 하나의 opt로는 둘 다 만족 못 함. 구성: 프로파일0=min1/opt1/max4(저지연용), 프로파일1=min8/opt32/max32(처리량용)을 한 엔진에. 운영: 프로파일당 별도 execution context를 두고, 대화형 요청은 context0(profile0)으로 작은 배치, 야간 배치는 context1(profile1)로 큰 배치 실행. 라우팅·동시성 관리는 Triton(5.12)으로. 메모리는 두 프로파일 합이라 여유 확인.
연습 4 (shape tensor). 동적 해상도를 받는 세그멘테이션 모델을 빌드하니 Resize 레이어에서 실패한다. 원인 후보와 진단·해결을 설명하라.
정답
원인 후보: Resize의 목표 크기가 shape tensor(형상 값 입력) 인데 동적이고, 그 값의 프로파일(min/opt/max)·런타임 주입을 안 했다. 진단: polygraphy inspect model로 어떤 입력이 shape tensor인지 확인. 해결: ① 그 shape tensor에 대해 프로파일에서 값의 min/opt/max를 지정하고 런타임에 set_shape_input(버전별 API)으로 값 주입, 또는 ② 가능하면 export 단계에서 형상을 execution tensor 기반(스케일·상수) 으로 바꿔 shape tensor 의존을 없앰. 해상도 종류가 소수면 정적 엔진 여러 개도 대안.
연습 5 (빌드 손잡이). 같은 구조 모델을 가중치만 바꿔 자주 재배포한다(파인튜닝 반복). 매번 풀 빌드(오토튜닝)가 부담이다. 두 가지 이상 완화책과 트레이드오프.
정답
① REFIT 가능 엔진으로 빌드해 두고, 가중치만 재빌드 없이 교체(refit) → 오토튜닝 생략. 트레이드오프: refit 엔진은 약간의 성능 손실 가능, 구조가 같아야 함. ② timing cache 재사용(같은 GPU·TRT) → 풀 빌드해도 오토튜닝 측정은 캐시에서 → 빌드 시간 단축. ③ 개발 중 optimization level↓로 빠르게 반복, 최종 배포만 레벨↑. 조합: REFIT로 일상 교체 + 주기적으로 풀 빌드(level↑·캐시)로 성능 리프레시. 핵심은 "구조 불변·가중치 변화"를 이용해 오토튜닝 반복을 피하는 것.
5.3.13 요약 · 다음 장
- 동적 shape는 가변 부하(배치·시퀀스·해상도)를 하나의 엔진으로 서빙한다. 대가는 "범위가 넓을수록 형상별 최적화가 무뎌짐" — 실제 분포에 범위를 맞추는 것이 핵심.
- optimization profile(min/opt/max): 빌더는 opt 형상에 맞춰 tactic을 고르고, max 기준으로 메모리를 할당한다. opt는 트래픽 봉우리에, max는 실제 상한에.
- 분포가 갈리면 다중 프로파일(프로파일당 별도 context). shape tensor(형상 값 입력)는 값의 프로파일·주입이 따로 필요 — 동적 Reshape/Resize 실패의 단골 원인.
- 빌더 손잡이: optimization level·timing cache·tactic source·builder flag(FP16·INT8·SPARSE·REFIT). flag는 하드웨어·조건이 맞아야 실효 — 켜고 끝이 아니라 실측.
- 정직: API·flag는 버전 의존(확인 필요), 모든 성능·메모리 수치는 실측, 가변 시퀀스 LLM은 일반 TRT가 아니라 TRT-LLM(5.7~).
다음 장: 5.4 TensorRT 정밀도 I — INT8 PTQ·캘리브레이션. 사면체의 정밀도 축을 본격적으로 판다. FP16을 넘어 INT8 후처리 양자화(PTQ) 로 처리량·메모리를 더 밀되, 캘리브레이션으로 정확도를 지키는 법 — 스케일 산정·캘리브레이션 데이터·태스크 지표 검증을 실습으로 따라간다.