TT Lab
요금제

한국어

시작하기
블로그

블로그

그래프 컴파일러와 런타임: 연산 융합, torch.compile, ONNX Runtime·TensorRT

상태: 초안 (2026-10-16), 일부 실측. torch.compile·메모리 배치·cuDNN 설정은 집 랩의 8GB급 노트북용 GPU 한 장에서 1회용 파드로 쟀습니다(PyTorch 2.9, 예열 20회 뒤 50회 × 5묶음의 중앙값). 융합의 원리는 노트북 CPU 예제로 확인했습니다(code/03_fusion.py, 몇 초). ONNX Runtime 과 TensorRT 는 실험 이미지에 없어 미실측입니다(내려받지 않았습니다). 시리즈 「비전 모델 읽기 3」 3편. 정밀도 줄이기는 2편에 있습니다.

목차

1. 직관: 요리사와 냉장고

GPU 의 계산 장치는 빠르지만, 데이터는 칩 밖의 메모리(냉장고)에 있습니다. 즉시 실행(eager)은 「합성곱 → 배치 정규화 → ReLU → 더하기」 를 각각 따로 합니다. 합성곱 결과를 냉장고에 넣고, 배치 정규화가 꺼내 고치고 다시 넣고, ReLU 가 또 꺼내고 넣습니다. 큰 특징 지도라면 계산보다 넣고 꺼내는 시간이 깁니다. 연산 융합은 「꺼낸 김에 넷을 다 하고 한 번만 넣는」 것입니다.

연산 융합과 컴파일러·런타임 융합 전: 연산마다 커널 하나, 메모리를 오간다 합성곱 BN ReLU 더하기 GPU 메모리 — 읽고 쓰기 4번, 커널 부르기 4번 융합 뒤: 한 커널이 값을 칩 안에 둔 채 끝까지 합성곱 + BN(가중치에 접음) + ReLU + 더하기 GPU 메모리 — 읽고 쓰기 1번, 커널 부르기 1번 같은 모델이 GPU 커널이 되기까지 두 길 PyTorch 모델 torch.compile 그래프 잡기 Inductor Triton 커널 생성 ONNX 파일 표준 그래프 ONNX Runtime TensorRT 엔진
그림: 융합 전에는 연산마다 커널 하나가 메모리를 오가고, 융합 뒤에는 한 커널이 값을 칩 안에 둔 채 끝까지 간다. 아래는 PyTorch 모델이 torch.compile 또는 ONNX 를 거쳐 GPU 커널이 되는 두 길

융합에는 두 종류가 있습니다.

BN 접기:  y = γ·(W∗x + b − μ)/√(σ²+ε) + β
        = (γ/√(σ²+ε))·W ∗ x + (γ·(b − μ)/√(σ²+ε) + β)
        = W' ∗ x + b'           ← 합성곱 하나로

2. 직접 해 보기(노트북 CPU)

code/03_fusion.py 를 노트북 CPU 스레드 하나로 돌렸습니다(7회 중앙값).

실험 결과
합성곱(32→64, 3×3, 입력 144×256) + BN 을 접기 최대 차이 4.77e-06(FP32 반올림 수준). 시간 5.80 → 5.80ms(차이 없음)
원소별 사슬 relu(x·1.5 + 0.5)·0.9, 원소 1,677만 개 즉시 실행 9.7ms → torch.compile 2.4ms(4.1배), 결과 차이 0
같은 사슬을 손으로 제자리 연산 7.4ms
원소 102만 개 더하기를 1,000번 나눠 부르기 0.13 → 0.88ms, 부를 때마다 약 0.8µs

3. 그래프 컴파일러는 무엇을 하나

TVM(Chen 외, 2018) 은 모델을 그래프로 받아 연산을 넷으로 나눕니다. 원소마다 하는 일(injective), 합치는 일(reduction), 출력에 원소별 일을 붙일 수 있는 복잡한 일(합성곱 같은 complex-out-fusable), 그리고 묶을 수 없는 일. 규칙에 따라 앞의 셋을 묶습니다. 연산 융합만으로 1.2–2배 빨라졌고(2015년 데스크톱급 GPU, 합성곱+BN+ReLU 등 작업 4개, 3절·그림 4), 다른 프레임워크 대비 끝단 1.6–3.8배였습니다(6.1절).

PyTorch 2(Ansel 외, 2024) 의 torch.compile 은 두 부분입니다.

데이터센터 GPU 에서 TIMM 이미지 모델 62개의 추론이 즉시 실행보다 FP32 2.48배, FP16 2.77배 빨랐습니다(표 3). 융합과 인라인을 둘 다 끄면 오히려 0.80배로 즉시 실행보다 느려졌습니다(표 4, HuggingFace FP16 추론). 속도의 대부분이 융합에서 온다는 뜻입니다. 초록의 「추론 2.27배」 는 어느 조합의 평균인지 본문에서 확인하지 못해 여기서는 표 3·4 의 값만 씁니다.

4. ONNX 와 런타임: 모델을 들고 다니는 형식

torch.compile 은 PyTorch 안에서 돕니다. 서버에 파이썬과 PyTorch 를 두고 싶지 않거나, 브라우저·폰에서 돌려야 하면 모델을 표준 그래프 파일로 꺼내야 합니다.

5. 우리 실측: 공 모델에 torch.compile

TT Score 의 공 모델(BlurBall, 3프레임 창, 512×288, 배치 1)을 실험용 GPU 에서 쟀습니다.

실행 방식 FP32 FP16 첫 부름(컴파일)
즉시 실행(지금 운영 방식) 15.09ms 9.77ms —
즉시 실행, cuDNN 자동 고르기 끔 15.08ms 9.75ms —
즉시 실행, channels_last(NHWC) 18.62ms 9.02ms —
torch.compile 기본 12.91ms(1.17배) 4.61ms(2.12배) 10–11초
torch.compile reduce-overhead(CUDA 그래프) 12.55ms 4.34ms(2.25배) 7–8초
torch.compile max-autotune(CUDA 그래프 없이) 12.79ms 4.48ms 41–46초

(FP16 은 autocast. 배수는 같은 정밀도의 즉시 실행 대비.)

같은 실행을 프로파일러로 보니 즉시 실행 FP32 한 번에 CUDA 커널 478개가 불렸고, GPU 가 일한 시간 14.88ms 중 합성곱이 8.09ms(72번), 배치 정규화가 3.27ms(71번, 22%)였습니다. 커널 시간의 합이 벽시계 시간(15.09ms)과 거의 같으므로 GPU 는 쉬지 않았습니다. 즉 부르기 비용이 아니라 메모리를 오가는 원소별 연산이 문제였고, 컴파일러가 이것을 묶어 없앤 것으로 해석합니다(컴파일된 판의 커널 수는 세지 않았습니다).

읽는 법입니다.

6. 한계

7. 우리 프로젝트와의 연결

티티 한마디. 똑같은 요리를 냉장고 문 덜 열고 했더니 두 배 빨라졌어요. 재료(가중치)도 레시피(모델)도 그대로인데요!

참고 문헌

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다