실시간 RAG 시스템의 추론 병목을 해결하기 위한 Computation Graph Optimization 기법을 정리했습니다. Operator Fusion부터 Memory 최적화까지, 실전 적용 가능한 핵심 전략을 소개합니다.
최근 Transformer 기반 모델의 실시간 추론 효율이 중요한 이슈로 떠오르고 있습니다. 이에 따라 Computation Graph 최적화에 대한 관심도 높아지고 있는데요, 이번 글에서는 이 주제를 2편에 걸쳐 자세히 다뤄보려 합니다.
Transformer 기반 모델은 고정밀 matrix 연산과 반복적인 self-attention 구조를 포함하고 있어, 추론 과정에서 latency가 크게 발생합니다. Retrieval-augmented generation (RAG) 시스템은 사용자 질의에 대한 빠른 응답이 요구되기 때문에, 이러한 추론 지연은 전체 응답 품질에 직접적인 영향을 미치게 됩니다.
대부분의 모델은 PyTorch 기반으로 구현되어 있으며, dynamic computation graph 구조를 사용합니다. 이 구조는 개발 단계에서는 유연성을 제공하지만, 추론 성능 최적화 측면에서는 비효율적인 측면이 존재합니다. 실시간 처리가 중요한 RAG 환경에서는 이러한 부분이 성능 병목으로 이어질 수 있습니다.
RAG 파이프라인에서 추론 지연이 집중되는 구간은 크게 두 가지입니다. 하나는 query를 encoding하는 bi-encoder 단계이며, 다른 하나는 candidate 문서를 재정렬하는 cross-encoder 단계입니다.
특히 cross-encoder는 query와 document를 함께 인코딩하는 구조이기 때문에 계산량이 많고, 후보 문서 수에 따라 추론 시간이 선형적으로 증가합니다. 따라서 per-query latency를 줄이기 위한 구조적 최적화가 필수적이며, 이를 위해 static graph 변환과 compiler 수준의 최적화 적용이 요구됩니다.
RAG 시스템에서의 실시간 처리를 위해 다음과 같은 최적화 목표가 설정됩니다:
이를 위해 적용 가능한 최적화 기법으로는 operator fusion, constant folding, precision reduction(FP16/INT8), memory planning 등이 있습니다.
또한 이러한 최적화를 실현하기 위해 TorchScript(JIT 기반 정적 변환), ONNX(중간 표현 기반 최적화), TensorRT(NVIDIA GPU 환경에 최적화된 고성능 추론 엔진)와 같은 프레임워크가 활용됩니다.
딥러닝 모델은 연산과 데이터 흐름을 Directed Acyclic Graph(DAG) 형태로 표현할 수 있습니다. 그래프의 노드는 연산자(operator), 엣지는 텐서(tensor)의 흐름을 나타냅니다.
학습(training) 과정에서는 autograd 엔진이 동적 그래프(dynamic graph)를 사용하여 gradient를 계산합니다. 반면, 추론(inference) 단계에서는 동일한 연산이 반복되므로, 정적 구조(static graph)로 변환할 경우 최적화에 유리합니다.
PyTorch는 기본적으로 dynamic graph 방식을 채택하고 있지만, TorchScript나 ONNX를 통해 모델을 export하면 static graph로 변환할 수 있습니다.
Transformer 모델은 self-attention, feed-forward network(FFN), residual connection, layer normalization 등의 블록으로 구성됩니다.
이들 블록에서 수행되는 주요 연산으로는 matrix multiplication(MatMul), softmax, GELU, normalization 등이 있으며, 이들은 대부분 높은 메모리 대역폭과 연산량을 요구합니다. 특히 attention 연산은 시퀀스 길이 n에 대해 O(n²)의 시간복잡도를 가집니다.
encoder-only 모델(BERT)은 입력 전체를 한 번에 인코딩하지만, decoder-only 모델(GPT 계열)은 auto-regressive 방식으로 토큰 단위의 반복 연산을 수행하게 됩니다. 이로 인해 inference 단계에서 latency가 누적되는 구조적 한계가 존재합니다.
연산 그래프를 static 형태로 고정하면 전체 연산 흐름을 사전에 분석할 수 있어 다양한 최적화가 가능합니다. 대표적인 최적화 기법으로는 다음과 같습니다:
또한 반복되는 구조를 unroll하거나 입력 shape이 고정된 경우 shape inference를 통해 memory allocation을 정적으로 구성할 수 있습니다.
dynamic graph는 유연성 면에서는 장점이 있지만, Python 인터프리터의 오버헤드와 GIL(Global Interpreter Lock)로 인해 최적화 여지가 제한적입니다. 반면 static graph로 변환하면 Python-free 추론이 가능해지며, multithreaded 실행과 low-level inference engine(TensorRT 등)에도 적합한 구조를 구성할 수 있습니다.
RAG 시스템에서의 추론은 다음과 같은 두 시점에서 발생합니다:
특히 cross-encoder는 질의와 각 문서를 결합해 개별 추론을 수행하므로, 문서 수가 증가할수록 추론 지연이 선형적으로 누적됩니다.
실시간 검색 환경에서는 일반적으로 300~500ms 이내의 지연이 요구되며, 이 범위를 초과할 경우 사용자 경험에 직접적인 영향을 미치게 됩니다. 따라서 미세한 수준의 연산 최적화도 운영 품질 측면에서 매우 중요합니다.
Computation graph는 모델 추론 시 수행되는 연산들의 정적 실행 흐름을 정의하며, latency 및 throughput 개선을 위한 최적화의 핵심 대상입니다. 특히 Transformer 기반 모델에서는 self-attention과 projection 계층 등의 반복적인 연산 구조로 인해, 효율적인 그래프 최적화가 실시간 응답 성능 향상에 직결됩니다.
정적 그래프(static graph)를 기반으로 한 최적화는 연산 구조의 간소화, 메모리 접근 감소, 실행 순서 개선, 연산 정밀도 변경 등을 통해 추론 효율을 높일 수 있습니다. 이러한 최적화는 단일 연산자 수준이 아니라 subgraph 단위로 적용될 때 latency 개선 효과가 더욱 큽니다.
Computation graph 최적화 기법은 일반적으로 다음의 다섯 범주로 분류할 수 있습니다:
1. 연산 구조 최적화 (Operator-level Optimization)
2. 데이터 흐름 최적화 (Dataflow Optimization)
3. 메모리 및 자원 최적화 (Memory & Resource Optimization)
4. 정밀도 축소 (Precision Reduction)
5. Transformer 특화 최적화
이러한 최적화 기법은 정확도 손실 없이 latency를 줄이는 conservative 방식부터, precision trade-off를 수용해 extreme throughput을 추구하는 aggressive 방식까지 다양하게 활용됩니다.
대부분의 기법은 static graph를 전제로 적용되며, ONNX, TorchScript, TensorRT 등의 inference backend에서도 공통적으로 지원되거나 유사한 형태로 구현 가능합니다.
Computation graph 최적화의 첫 단계는 연산자 수준에서 불필요한 연산을 제거하거나, 연산을 합쳐 효율적으로 실행하는 것입니다.
이 과정은 대부분 static graph 상에서 정적으로 분석되며, 특히 Transformer처럼 반복되는 연산 패턴이 많은 구조에서는 subgraph 단위 최적화가 latency를 줄이는 데 매우 효과적입니다.
대표적으로 많이 활용되는 연산자 최적화 기법은 다음 세 가지입니다: Operator Fusion, Constant Folding, Dead Code Elimination
Operator Fusion은 연속된 연산자들을 하나의 fused kernel로 합쳐 실행하는 기법입니다.
이렇게 하면 연산 간 메모리 이동을 줄이고 kernel 호출 수를 줄일 수 있어 GPU 자원을 보다 효율적으로 쓸 수 있습니다.
Transformer 구조에서는 Linear → Add → LayerNorm
이나, Q/K/V projection → Concat → Transpose
같은 연산 흐름이 반복적으로 등장합니다.
이들을 각각 따로 실행하면 연산량도 많고 메모리 접근도 자주 발생해 비효율적입니다.
이런 경우, 연산들을 하나로 묶어 FusedLinearNorm
또는 FusedQKVProjection
형태의 단일 커널로 최적화할 수 있습니다.
이 최적화는 TorchScript, ONNX Runtime, TensorRT 같은 백엔드에서 자동으로 적용되는 경우도 많습니다.
다만 모든 구조에 적용할 수 있는 건 아니고, 아래 조건에서만 안정적으로 적용됩니다.
Constant Folding은 계산 결과가 항상 일정한 연산을 미리 계산해두고, runtime에서 다시 계산하지 않도록 하는 기법입니다.
Transformer 구조에서는 rotary embedding, attention mask, normalization 초기값처럼 반복 수행되지만 변하지 않는 연산들이 많습니다.
이러한 연산을 export 시점에 미리 계산해서 static tensor로 바꿔두면, runtime에서 별도 연산 없이 indexing만으로 처리할 수 있습니다.
예를 들어 rotary embedding의 경우, sin/cos 값을 토큰마다 계속 계산하는 대신, export 단계에서 미리 만들어 넣어두면 추론 시에는 lookup만 수행하면 됩니다.
이 기법은 전체 latency를 획기적으로 줄이기보다는, 불필요한 연산을 제거하고 그래프를 단순화해 latency를 안정화하는 데 효과적입니다.
Dead Code Elimination (DCE)은 추론 시점에 실제로 사용되지 않는 연산을 그래프에서 완전히 제거하는 작업입니다.
보통 model.eval()
을 사용하면 Dropout이나 BatchNorm 같은 연산이 runtime에서 "비활성화"되긴 하지만, 연산자 자체는 여전히 그래프에 남아 있는 경우가 많습니다.
DCE는 이처럼 사용되지 않는 연산자와 노드를 아예 제거하여, memory 사용량과 kernel 호출을 줄여줍니다.
예를 들어 Dropout은 학습 시에는 필요하지만 추론에서는 무의미합니다.
그런데 이 연산이 그래프 안에 남아 있으면, 메모리도 차지하고 실행 오버헤드도 생깁니다.
그래서 ONNX export 시점에서 Dropout 연산 자체를 제거해 그래프를 정리하면, 훨씬 효율적인 추론이 가능해집니다.
[예시 3.1.1] FusedLinearNorm
문제
Transformer block에서는 다음과 같은 연산이 순차적으로 수행됩니다: x → Linear(W) → Add(bias) → Add(residual) → LayerNorm(γ, β)
이 연산 흐름은 개별 연산자가 각각 별도의 kernel로 실행되며, 중간 결과가 GPU 메모리에 반복 저장됩니다.
특히 Layer 수가 많을수록 memory access cost와 kernel launch overhead가 누적됩니다.
해결
이러한 연산 흐름은 하나의 fused kernel로 병합하여 처리할 수 있습니다.
일반적으로 FusedLinearNorm
이라는 연산자 형태로 구현되며, 다음과 같은 구조로 합쳐집니다: y = LayerNorm(Linear(x) + bias + residual)
이 최적화는 TorchScript, ONNX Runtime, TensorRT 등에서 연속된 연산 패턴을 정적으로 감지한 뒤 자동으로 적용됩니다.
적용 효과
[예시 3.1.2] FusedQKVProjection
문제
Self-attention에서는 입력 x에 대해 다음과 같은 연산이 수행됩니다: x → Linear(W_q), Linear(W_k), Linear(W_v) → concat → transpose
이는 3개의 Linear 연산과 그 뒤를 잇는 concat 및 transpose 연산으로 나뉘며, 각 단계마다 별도의 memory copy 및 kernel call이 발생합니다.
해결
3개의 Linear 연산을 하나의 weight 행렬 W_qkv
로 합쳐서 projection을 한 번에 처리하고,
concat 및 transpose 연산도 포함한 fused kernel을 적용할 수 있습니다.
TensorRT의 MHA(Multi-head Attention) Plugin이나 HuggingFace Optimum의 ONNX 패턴 최적화 시에도 이 방식이 활용됩니다.
적용 효과
[예시 3.1.3] PrecomputedRotaryEmbedding
문제
Rotary positional embedding은 다음과 같은 연산을 반복 수행합니다: positional angle θ → sin(θ), cos(θ) → elementwise embedding 곱
이 과정은 매 토큰마다 sin
, cos
를 계산하고 embedding 벡터에 적용하기 때문에,
시퀀스 길이가 길어질수록 trigonometric 함수 호출로 인한 latency가 증가합니다.
해결
Rotary embedding에 사용되는 sin, cos 값을 모델 export 시점에 미리 계산한 고정된 tensor로 삽입합니다.
이렇게 하면 runtime에는 단순히 indexing만 수행하게 되어, 연산 비용이 크게 줄어듭니다.
ONNX export 중 constant folding 단계에서 적용됩니다.
적용 효과
[예시 3.1.4] DropoutRemoval (Dead Code Elimination)
문제
Dropout 연산은 학습 시에는 중요한 regularization 기법이지만,
inference 시점에는 무의미한 연산입니다. 그럼에도 불구하고 Dropout 연산이 그래프에 남아 있다면,
다음과 같은 구조가 유지됩니다: x → Linear → Dropout → Add → LayerNorm
이 경우 Dropout을 위한 memory buffer가 생성되고, 실행 경로도 유지되기 때문에 오버헤드가 발생합니다.
해결
ONNX로 export 시, Dead Code Elimination(DCE) pass를 통해 Dropout 노드를 완전히 제거합니다.
Dropout의 입력과 출력을 직접 연결하여, Dropout 연산 자체가 실행 그래프에서 제거됩니다.
적용 효과
Dataflow-level 최적화는 연산 간 데이터 이동을 최소화하고, 중복된 연산을 제거하는 데 초점을 둡니다. 모델 구조를 바꾸는 것이 아니라, 연산이 흘러가는 경로를 최적화하는 방식이라고 이해하시면 됩니다.
Transformer 구조에서는 attention, residual, projection 등 다양한 블록에서 shape 변경
, transpose
, broadcast
연산이 자주 사용되는데, 이런 연산은 눈에 띄는 연산량은 적어도 memory bandwidth와 latency에는 큰 영향을 줍니다.
여기서는 세 가지 대표적인 최적화 기법을 소개합니다:
Transpose Elimination,
Shape Inference,
Common Subexpression Elimination
Transpose 연산은 연산량 자체는 작지만, memory layout을 바꾸기 때문에 memory 이동이 많고 GPU 캐시 효율이 떨어집니다. 특히 Transpose → Transpose
처럼 inverse pair가 반복되는 경우, 의미 없는 연산만 늘어나게 됩니다.
예를 들어 다음과 같은 경우를 생각해볼 수 있습니다:
x → Transpose(A→B) → Transpose(B→A)
→ 결과적으로 아무 일도 안 일어난 것과 같음 MatMul
앞뒤로 Transpose가 삽입되어 있는데, weight의 layout을 바꾸면 Transpose 자체를 생략할 수 있음
이런 경우 Transpose 연산을 제거하고 tensor를 다음 연산에 직접 연결하면 unnecessary memory copy를 줄일 수 있습니다.
실제로 multi-head attention에서는 head 간 layout 전환에서 상당한 overhead가 발생하는데, 이걸 줄이는 데 효과적입니다.
딥러닝 모델에서는 Expand
, Unsqueeze
, Reshape
같은 연산이 생각보다 자주 등장합니다.
이 연산들이 runtime에 shape를 계산하게 되면, 작은 연산이라도 누적될수록 overhead가 생기게 됩니다.
Shape Inference는 static graph의 특성을 이용해서 모든 tensor의 shape를 export 시점에 확정하는 기법입니다.
즉, 연산 시점에 shape를 계산하는 게 아니라, 그래프 상에서 미리 계산해두는 것이죠.
예를 들어, 특정 입력이 항상 (batch_size, 512)
라고 가정할 수 있다면,
이 input에 적용되는 모든 reshape
, expand
연산의 결과도 미리 계산해서 고정된 연산자로 바꿔줄 수 있습니다.
이렇게 하면 runtime에서 shape 연산 자체를 생략할 수 있고, memory도 사전에 미리 할당 가능해집니다.
Common Subexpression Elimination, 줄여서 CSE는 말 그대로 중복되는 연산을 한 번만 수행하고 결과를 재사용하는 기법입니다.
Transformer 모델처럼 구조가 반복되는 경우, 동일한 연산이 여러 번 수행되는 일이 자주 발생합니다.
대표적인 예시는 다음과 같습니다:
이런 경우, mask나 embedding을 한 번만 계산하고 모든 layer에서 재사용할 수 있도록 그래프를 구성하면 됩니다.
연산량 자체가 줄어들고, 메모리도 절약되며, 실행 trace도 더 단순해지는 장점이 있습니다.
단, 이 최적화는 연산이 pure function일 때만 안전하게 적용할 수 있습니다.
즉, 같은 입력이 들어오면 항상 같은 출력이 나오고, side-effect가 없어야 합니다.
이번에는 실제로 자주 등장하는 데이터 흐름 최적화 예시들을 정리해보겠습니다.
특히 Transformer 구조에서는 attention 블록, projection, reshape 연산 등이 반복되기 때문에,
이런 부분에서 불필요한 연산을 제거하는 것만으로도 꽤 큰 성능 개선을 얻을 수 있습니다.
[예시 3.2.1] Transpose Pair 제거
Transformer의 Multi-head Attention에서는 head dimension을 맞추기 위해 Transpose 연산을 반복하는 경우가 많습니다.
예를 들어 다음과 같은 연산 흐름이 있다고 해봅시다:
x → Linear → Transpose(0,2,1,3) → ScaledDotProductAttention → Transpose(0,2,1,3) → Output
여기서 두 개의 Transpose가 서로 역관계처럼 보이지만, 실제로는 같은 perm을 반복해서 호출하는 구조입니다.
결과적으로는 원래와 동일한 레이아웃을 계속 유지하면서, 불필요하게 메모리 이동을 두 번이나 수행하게 되는 셈이죠.
이런 경우 두 Transpose 연산을 제거하고, attention 연산의 입력 layout을 직접 맞춰주는 방식으로 최적화할 수 있습니다.
최적화 전
x → Transpose(0,2,1,3) → Attention → Transpose(0,2,1,3)
최적화 후
x → Attention (입력 layout 직접 매핑)
이 최적화는 TensorRT나 ONNX optimizer의 eliminate_nop_transpose
같은 pass에서 자동으로 수행됩니다.
[예시 3.2.2] Transpose + MatMul Layout 통합
PyTorch의 Linear 연산은 내부적으로 다음처럼 구현됩니다:
y = x @ W.T + b
하지만 ONNX로 export할 경우 .T
연산이 명시적인 Transpose로 고정되며, 결국 불필요한 Transpose 연산자가 graph에 삽입됩니다. 예를 들어:
x: (B, S, D_in) → Transpose(x) → MatMul(W) → Output
이때 W는 학습 시 고정된 weight이므로, Transpose를 x에 적용하는 대신 W 자체를 미리 Transpose해둘 수 있습니다.
즉, MatMul(x, W.T)
를 그대로 표현하는 대신 MatMul(x, new_W)
로 표현해 Transpose 연산을 생략하는 것입니다.
최적화 전
x → Transpose → MatMul(W)
최적화 후
x → MatMul(W.T)
이 방식은 memory 이동을 줄이고, weight layout을 고정함으로써 tensor 연속성을 확보할 수 있다는 장점이 있습니다.
[예시 3.2.3] Static Shape Inference
Transformer에서는 Expand
, Unsqueeze
, Reshape
같은 shape 관련 연산이 많이 사용됩니다.
이들 연산은 PyTorch에서는 자동으로 처리되지만, ONNX나 TensorRT에서는 실행 시점에 shape를 계산해야 해서
추론 시 오버헤드가 생기게 됩니다.
예를 들어 다음과 같은 흐름이 있다고 합시다:
x → Unsqueeze(dim=1) → Expand(…, H, …) → Reshape → Linear
이런 경우, export 전에 입력 shape를 고정하고, 모든 shape 관련 연산에 정수 constant를 사용하면
runtime shape 계산을 완전히 없앨 수 있습니다.
최적화 전
x: dynamic shape → shape 계산 연산 → Reshape(x, shape)
최적화 후
x: static shape → Reshape(x, [4, 64, 128])
이 방식은 memory를 미리 할당할 수 있고, 추론 경로를 단순화할 수 있는 효과가 있습니다.
[예시 3.2.4] Attention Mask 중복 제거 (CSE)
Transformer에서는 각 layer가 동일한 attention mask를 사용하지만,
그래프 export 시 각 layer가 동일한 연산을 반복 수행하는 구조로 만들어지는 경우가 많습니다.
결과적으로 identical한 mask가 layer마다 중복 생성되어 비효율적인 실행이 발생합니다.
이 경우 mask를 한 번만 계산하고 모든 layer에서 공유하도록 graph를 재구성할 수 있습니다.
최적화 전
input_ids → mask_fn → attn_mask_1
→ mask_fn → attn_mask_2
→ mask_fn → attn_mask_3 ...
최적화 후
input_ids → mask_fn → shared_attn_mask
↓ ↓ ↓
모든 attention block
이 방식은 연산량을 줄이는 동시에 그래프를 단순화하고 메모리 사용량도 절감할 수 있습니다.
ONNX에서는 common_subexpression_elimination
pass로 이런 최적화를 적용할 수 있습니다.
추론 환경에서는 모델 정확도만큼이나 중요한 것이 메모리 사용량과 실행 안정성입니다. 특히 Transformer 기반 모델은 시퀀스 길이나 히든 차원 크기 등으로 인해 중간 activation tensor가 매우 커질 수 있어, 메모리 최적화가 곧 성능이라고 해도 과언이 아닙니다.
이 섹션에서는 inference 효율을 높이기 위한 네 가지 메모리 최적화 기법을 다룹니다.
핵심은 불필요한 버퍼를 줄이고, 가능한 자원을 공유하거나 낮은 정밀도를 사용하는 방식으로 효율성을 높이는 것입니다.
모델의 연산 흐름을 따라가다 보면, 중간 결과로 생성된 tensor가 더 이상 쓰이지 않는데도 계속 메모리에 남아있는 경우가 있습니다.
Memory Reuse는 이러한 tensor들의 생존 주기(lifetime)를 분석해, 이후 연산에서 같은 메모리 공간을 재활용하는 방식입니다.
예를 들어 Transformer block에서 다음과 같은 연산이 있다고 해보죠:
Linear → GELU → Linear → Add → LayerNorm
GELU의 입력은 이후에 다시 사용되지 않기 때문에, 이 메모리 공간을 다음 Linear 연산에서 재활용할 수 있습니다.
이런 방식은 static graph 상에서 live-range 분석을 통해 자동으로 최적화되며, peak memory 사용량을 상당히 줄일 수 있습니다.
보통 연산 결과는 새로운 메모리 버퍼에 저장되지만, 때로는 입력 tensor를 그대로 덮어쓰는 방식으로 memory 사용을 줄일 수 있습니다.
이를 Inplace Computation이라고 하며, PyTorch에서는 inplace=True
옵션으로, ONNX나 TensorRT에서도 일부 연산에서 지원합니다.
예를 들어 LayerNorm 연산은 일반적으로 다음처럼 수행됩니다:
out = LayerNorm(x)
하지만 x가 이후에 사용되지 않는다면 아래와 같이도 쓸 수 있습니다:
LayerNorm(x, out=x)
이렇게 하면 불필요한 메모리 복사를 막을 수 있고, 전체 메모리 footprint도 줄어듭니다.
모델 구조를 보면 positional embedding이나 query/key projection weight처럼, 완전히 동일한 값이 여러 블록에 중복 포함되는 경우가 많습니다.
Weight Sharing은 이런 constant 또는 weight tensor들을 복제하지 않고, 하나의 메모리 참조로 공유하는 방식입니다.
예를 들어 Q와 K의 projection weight가 동일할 경우, 별도로 저장하는 대신 하나의 buffer를 공유하게 만들 수 있습니다.
이렇게 하면 전체 모델 크기를 줄일 수 있을 뿐만 아니라, GPU 메모리 초기화 속도도 개선됩니다.
Constant Folding vs Weight Sharing
이 두 기법은 자주 혼동되지만, 적용 대상과 방식이 다릅니다.
Constant Folding은 연산 결과를 미리 계산해서 runtime 연산 자체를 없애는 방식이고,
Weight Sharing은 동일한 tensor를 여러 번 복제하지 않고 하나로 참조하는 방식입니다.
추론에서는 꼭 모든 연산을 32비트 float(FP32)로 할 필요는 없습니다.
많은 경우 16비트 부동소수점(FP16, BF16)으로도 충분히 안정적인 결과를 얻을 수 있으며, 이때 memory 사용량과 연산량이 함께 줄어드는 장점이 있습니다.
하지만 일부 연산은 여전히 높은 정밀도가 필요합니다. 대표적으로 LayerNorm
, Softmax
는 작은 값이나 큰 값을 다루기 때문에, FP16에서는 underflow나 overflow가 발생할 수 있습니다. 그래서 보통은 주요 연산만 FP32로 유지하고, 나머지는 FP16으로 전환하는 mixed precision 방식이 널리 쓰입니다.
AMP(Automatic Mixed Precision)나 loss scaling 같은 기법을 함께 사용하면 안정성을 확보할 수 있습니다.
[예시 3.3.1] FFN Block 내 Memory Reuse
Feed-forward block은 다음과 같은 구조로 이루어져 있습니다:
x → Linear1 → GELU → Linear2 → Add(residual) → LayerNorm
여기서 Linear1의 출력인 y1
은 GELU를 거친 이후에는 더 이상 사용되지 않지만, 대부분의 그래프에서는 y1
을 별도 tensor로 유지합니다. 이로 인해 불필요한 메모리 사용이 발생하고, 긴 입력 시퀀스에서는 OOM(Out-Of-Memory) 위험이 커집니다.
이 문제는 live-range 분석을 통해 해결할 수 있습니다. y1
의 생존 범위를 파악한 후, 이후 연산에서 이 메모리 버퍼를 재활용하도록 설정하면 됩니다.
TensorRT, ONNXRuntime 등의 프레임워크는 static memory planning을 통해 이 최적화를 수행합니다.
적용 효과
[예시 3.3.2] LayerNorm Inplace 처리
LayerNorm은 입력과 출력의 shape가 동일하기 때문에, 조건만 맞는다면 출력 값을 입력 tensor에 그대로 덮어쓸 수 있습니다.
# 일반적인 경우
y = LayerNorm(x)
# Inplace 적용
LayerNorm(x, out=x)
이렇게 처리하면 메모리 복사를 줄이고, 전체 메모리 사용량도 줄일 수 있습니다.
PyTorch에서는 inplace=True
옵션을 통해 가능하며, TorchScript나 ONNX로 export할 때도 graph rewrite를 통해 적용할 수 있습니다.
적용 조건
x
가 이후에 사용되지 않아야 함 적용 효과
[예시 3.3.3] Positional Embedding Weight Sharing
Transformer 구조에서 sin/cos positional embedding 또는 learnable embedding은 여러 layer에서 동일하게 사용됩니다.
하지만 ONNX로 export할 경우, 각 layer에 embedding이 복제되는 경우가 많아 불필요한 메모리 낭비가 발생합니다.
이를 해결하기 위해, embedding tensor를 하나의 shared buffer로 folding하고 그래프 상에서 모든 layer가 이를 참조하도록 변경할 수 있습니다.
TensorRT, ONNXRuntime에서는 constant folding 및 buffer deduplication pass를 통해 이 최적화를 자동 수행할 수 있습니다.
적용 효과
[예시 3.3.4] Mixed Precision 적용 (FP16)
Transformer 모델을 FP32로 실행하면 연산 정확도는 유지되지만, 메모리 사용량과 연산 비용이 높아집니다.
특히 large model, long sequence 환경에서는 GPU 리소스가 금세 한계에 도달합니다.
이 문제를 해결하기 위해 대부분의 연산을 FP16이나 BF16으로 변환하고, 민감한 연산(LayerNorm, Softmax 등)만 FP32로 유지하는 mixed precision 전략을 적용할 수 있습니다.
PyTorch AMP, ONNX precision pass, TensorRT builder 등에서도 이러한 설정을 지원합니다.
적용 조건
적용 효과
Execution graph 최적화는 모델 전체의 연산 흐름을 분석해, 불필요한 분기나 중복된 노드, 사용되지 않는 출력 등을 제거하고, 반복적인 subgraph 구조를 재구성하는 작업입니다.
Transformer처럼 고정된 구조가 반복되는 모델에서는 특히 효과가 큰 영역으로, 연산 효율뿐 아니라 graph 분석 속도, 메모리 사용량 개선에도 직결됩니다.
대표적으로 자주 적용되는 최적화 기법은 아래와 같은 네 가지입니다.
모델 내부에는 if
, Select
, Where
, Loop
같은 control flow 연산이 포함되어 있는 경우가 많습니다. 이 중 실행 시점에 항상 같은 조건을 따르는 분기가 있다면, 이를 제거하고 고정된 경로로 단순화할 수 있습니다.
예를 들어 decoder-only 모델에서는 cross-attention
여부를 if 조건문으로 다루는데, export 시점에 이미 decoder-only로 확정된 모델이라면 해당 분기 자체가 불필요합니다.
이런 control flow를 제거하면 그래프가 더 간결해지고, 실행 경로도 최적화됩니다.
ONNX, TorchScript 등에서는 이를 위한 최적화 패스를 기본 제공하며, 분기 제거만으로도 cross-attention 유무에 따라 latency가 차이 나기도 합니다.
의미 없는 노드를 제거하는 기법입니다.
예를 들어 다음과 같은 연산 흐름은 실질적인 계산을 하지 않지만, tracing이나 export 과정에서 종종 생성됩니다:
x → Cast(float32 → float32) → Identity → x
이처럼 입력과 출력이 동일한 연산(Cast, Identity 등)은 제거하는 것이 바람직합니다.
Dropout(p=0) 같은 연산도 inference 시점에서는 무의미하므로 pruning 대상이 됩니다.
이 작업은 노드 수 자체를 줄이고 graph size도 작게 만들어, compile 속도와 optimizer의 분석 시간을 단축시킬 수 있습니다.
Transformer 모델의 출력에는 보통 logits 외에도 hidden_states, attentions, past_key_values 등이 포함됩니다.
하지만 실제 서비스에서는 logits만 사용하는 경우가 대부분입니다.
이때 사용되지 않는 출력 값을 제거하면, 해당 output을 생성하는 연산도 같이 줄일 수 있습니다.
예를 들어 hidden_states를 생성하는 MLP나 projection 연산들이 제거 대상이 될 수 있죠.
output 수가 줄면 그래프가 간결해지고, memory 사용량과 latency가 줄어드는 이점도 따라옵니다.
Transformer 구조에서는 attention block이나 FFN block처럼 반복되는 연산 조합이 많습니다. 이런 subgraph들을 인식해서 하나의 최적화된 커널로 묶는 방식이 subgraph folding
입니다.
예를 들어 다음과 같은 attention subgraph:
QKV → MatMul → Scale → Softmax → MatMul → Add → Output
이 구조를 개별 연산으로 실행하면 memory 이동이 많고 launch overhead도 커집니다.
TensorRT나 ONNXRuntime EP 등에서는 이 패턴을 자동 인식해서 FusedAttention
같은 커스텀 op로 변환해주기도 합니다.
적용 조건만 맞는다면, 연산 수가 5~10개에서 1개로 줄고, 메모리 접근도 대폭 감소합니다.
[예시 3.4.1] Control Flow Simplification – decoder-only 조건 분기 제거
Transformer decoder 모델에서는 decoder_only
설정 여부에 따라 cross-attention을 실행할지 말지를 결정하는 조건 분기가 존재합니다.
if decoder_only:
cross_attention = None
else:
cross_attention = cross_attention_block(...)
이 구조는 ONNX로 export할 때 If
노드로 고정되는데, 실제로는 대부분의 경우 decoder_only=True
로 고정되어 있어 분기 경로가 항상 하나로 수렴합니다.
그럼에도 불구하고 ONNX 그래프에는 If
, Constant
, Identity
, 사용되지 않는 branch 등이 모두 포함돼 있어, 그래프가 불필요하게 복잡해지고 추론 성능에도 영향을 줍니다.
해결 방법
remove_unused_branch
패스로 제거 eliminate_deadend
, eliminate_branch
등이 자동 적용됨적용 효과
[예시 3.4.2] Redundant Node Pruning – Identity & Cast 제거
PyTorch 모델을 ONNX로 export하거나 TorchScript로 tracing할 때, 내부적인 tracing 목적으로 Identity
, Cast
연산이 삽입되는 경우가 자주 발생합니다.
예를 들어:
x → Cast(float32 → float32) → Identity → y
이런 연산들은 실제로는 아무런 효과가 없지만, 그래프에는 별도 노드로 존재하게 되며, 실행 시 불필요한 memory copy나 kernel 호출을 유발합니다. 특히 .to()
, .detach()
, .clone()
같은 연산들이 원치 않게 이런 노드로 바뀌는 경우가 많습니다.
해결 방법
eliminate_identity
, eliminate_nop_cast
최적화 패스 적용 remove_nop_nodes
, simplify_graph
등으로 제거 가능적용 효과
[예시 3.4.3] Dead Output Elimination – hidden_states 제거
HuggingFace 기반 Transformer 모델은 다음처럼 다양한 출력을 제공합니다:
return {
"logits": logits,
"hidden_states": all_hidden_states,
"attentions": all_attentions
}
그러나 실제 서비스 환경에서는 대부분 logits
만 사용되고, 나머지 출력은 전혀 쓰이지 않습니다.
그럼에도 export 시점에서 출력 시그니처를 명시하지 않으면, 모든 출력 경로가 그대로 그래프에 포함됩니다.
특히 hidden_states
는 모든 layer의 출력을 누적하므로, 연산량과 메모리 사용량이 크게 증가합니다.
해결 방법
hidden_states
, attentions
등을 제거 remove_unused_outputs
, eliminate_unused_initializer
패스 적용 적용 효과
[예시 3.4.4] Subgraph Folding – Fused Multi-Head Attention
Transformer에서 Self-Attention 블록은 다음처럼 여러 연산으로 구성됩니다:
x → Linear_q, Linear_k, Linear_v
→ MatMul(q, k^T) → Scale → Softmax → Dropout
→ MatMul(..., v) → Output projection
이 전체 subgraph가 독립된 연산자로 구성돼 있을 경우, 메모리 이동이 반복되고 launch overhead가 누적되면서 성능 저하로 이어집니다.
해결 방법
FusedMultiHeadAttention
같은 하나의 연산으로 folding 적용 조건
적용 효과
Precision Reduction은 연산자의 데이터 타입을 float32(FP32)에서 float16(FP16), bfloat16(BF16), int8 등으로 축소해
메모리 사용량과 연산량을 동시에 줄이는 최적화 기법입니다.
Transformer 구조는 대부분의 연산이 low-precision 환경에서도 안정적으로 수행되기 때문에,
실제 inference latency는 precision 및 memory bandwidth에 의해 좌우되는 경우가 많습니다.
정밀도 축소는 크게 세 가지 방식으로 나눌 수 있습니다:
Mixed Precision,
Dynamic Quantization,
Static Quantization
Mixed Precision은 모델의 주요 연산을 FP16 또는 BF16으로 변환해 실행하는 방식입니다.
이렇게 하면 메모리 사용량이 절반으로 줄어들고, GPU의 Tensor Core 같은 low-precision 연산 유닛을 활용할 수 있어 throughput도 향상됩니다.
Transformer 모델은 대부분 연산에서 정밀도 손실에 강건하기 때문에, mixed precision 적용이 비교적 수월한 편입니다.
적용 방식
주의할 점
Dynamic Quantization은 weight를 INT8로 변환하되, activation은 runtime에 실시간으로 quantize/dequantize하는 방식입니다.
즉, 실행 시 입력 분포를 기반으로 scale과 zero-point를 동적으로 계산합니다.
연산 흐름 예시
float_input → Quantize(scale, zero_point) → int8_input
→ Dequantize(scale, zero_point) → float → 연산 수행
장점
한계
Static Quantization은 weight뿐 아니라 activation도 모두 정수(INT8)로 고정하는 방식입니다.
이 경우 calibration dataset을 통해 연산별 입력/출력의 min/max 값을 미리 측정해야 하며,
quantization parameter는 export 시점에 삽입됩니다.
적용 흐름
장점
주의 사항
Transformer 구조는 attention, feed-forward, projection 등 대부분의 연산이 정밀도 축소에 잘 적응합니다.
따라서 상황에 따라 적절한 quantization 기법을 선택하면, 정확도를 유지하면서도 상당한 성능 개선을 기대할 수 있습니다.
[예시 3.5.1] Mixed Precision 적용 (FP16)
문제
Transformer 모델을 FP32로 실행할 경우, 모든 텐서가 4바이트 단위로 처리되어
GPU memory bandwidth에 큰 부담을 주게 됩니다. 또한, Tensor Core 등 low-precision 연산 장치를 활용하지 못해
고비용 연산(MatMul, Attention, Linear 등)의 latency가 커지는 원인이 됩니다.
특히 long sequence 처리나 large batch 처리 시, memory 부족(OOM)이 발생하기 쉬운 구조입니다.
해결 방법
autocast
기반의 AMP(Automatic Mixed Precision) 적용 enable_fp16
builder flag를 설정하면 자동으로 FP16 kernel이 적용됨적용 조건
적용 효과
주의사항
[예시 3.5.2] Dynamic Quantization 적용 (INT8)
문제
Transformer 모델에서 Linear, MatMul 연산이 전체 연산량과 memory usage의 상당 부분을 차지합니다.
특히 CPU 기반의 inference 환경에서는 연산 성능 확보가 어렵고, memory 효율도 낮습니다.
해결 방법
float_input → Quantize(scale, zp) → int8_input
→ Dequantize(scale, zp) → float_input → 연산
적용 조건
적용 효과
한계
[예시 3.5.3] Static Quantization 적용 (INT8)
문제
Transformer의 attention, feed-forward block, embedding projection 등은 연산량이 많고,
FP32 기반에서는 memory와 latency 모두 부담이 큽니다. 특히 GPU에서 INT8 연산을 적용하려면
activation 범위를 미리 고정해야 하는데, dynamic quantization 방식으로는 이를 처리할 수 없습니다.
해결 방법
float_input → Quantize → int8_input
× int8_weight
→ Dequantize → float_output
적용 조건
적용 효과
주의사항
이러한 정밀도 축소 기법들은 각각 장단점이 있으며, 사용하는 하드웨어, 모델 구조, 정확도 요구 수준에 따라
적절한 조합을 선택하는 것이 중요합니다. 특히 Transformer 계열 모델은 대부분 연산이 low-precision 환경에 잘 대응하므로,
성능 병목이 발생하는 상황에서 precision reduction은 가장 효과적인 최적화 중 하나가 될 수 있습니다.
Transformer 모델은 내부적으로 동일한 연산 구조가 반복되는 특징이 있습니다.
이러한 구조적인 특성 덕분에, 일반적인 연산 최적화 외에도 Transformer에 특화된 subgraph-level 최적화 기법을 적용할 수 있습니다.
특히 Attention, Rotary Embedding, LayerNorm 등은 연산 흐름이 고정되어 있어
컴파일 타임 혹은 그래프 최적화 시점에 보다 강력한 병합(fusion) 및 사전 계산(precomputation)이 가능합니다.
기법 개요
Transformer의 Multi-Head Attention은 Q, K, V 생성 → head 분리 → scaled dot-product → softmax → weighted sum → head 병합 등의 단계로 구성됩니다.
기본 구현에서는 각 단계가 개별 연산으로 처리되지만, 이들을 하나의 fused kernel로 통합하면 실행 효율이 크게 향상됩니다.
TensorRT, FlashAttention, ONNX Runtime EP 등에서는
이 subgraph를 통째로 감지해 FusedMultiHeadAttention 형태의 고성능 연산으로 대체할 수 있습니다.
적용 방식
적용 효과
기법 개요
Rotary Positional Embedding(RoPE)은 각 토큰의 위치 정보를 sin/cos 함수로 encoding합니다.
하지만 이 연산은 입력값과 무관하게 deterministic하게 반복되므로, runtime이 아닌 export 시점에 미리 계산할 수 있습니다.
적용 방식
적용 효과
기법 개요
GPT와 같은 decoder-only 모델에서는 토큰 생성 시 이전 시점의 Key, Value를 계속해서 누적합니다.
매 시점마다 K/V를 다시 계산하는 대신, 이미 계산한 값을 cache에 저장하고 재활용하는 방식으로 inference latency를 획기적으로 줄일 수 있습니다.
적용 방식
적용 효과
Transformer 구조에서는 다음과 같은 패턴들도 자주 등장하며, 이 역시 별도 연산자 수준이 아니라 구조 최적화로 처리할 수 있습니다.
특히 Multi-Head Attention block 전체를 fused op 또는 plugin 연산자로 대체하는 방식은 실제 환경에서 가장 큰 latency 개선 효과를 가져올 수 있으며, 대부분의 최신 inference backend에서도 가장 우선적으로 지원하는 최적화 대상입니다.
[예시 3.6.1] Fused Multi-Head Attention
문제
기존 Multi-Head Attention 구조는 Q/K/V projection부터 head 분리, dot-product, softmax, weighted sum, 그리고 최종 output projection까지
6~9개의 연산으로 분리되어 각각 kernel을 호출합니다.
이 과정에서 중간 tensor가 GPU memory에 반복 저장되며, kernel launch overhead도 누적됩니다.
특히 head 수가 많거나 sequence length가 긴 경우 성능 저하가 두드러집니다.
해결
FusedLinear
연산으로 처리 MultiHeadAttentionPlugin
, FlashAttention: CUDA kernel 기반 최적화, com.microsoft.FusedAttention
등에서 지원적용 효과
[예시 3.6.2] Rotary Embedding Precomputation
문제
RoPE는 sin/cos 함수를 기반으로 위치 정보를 인코딩하지만, 대부분의 구현에서는 이 값을 runtime에 매번 계산합니다.
특히 긴 sequence에서는 token 수만큼 반복적인 sin/cos 연산이 발생해 latency가 누적되고,
ONNX export 시에도 해당 연산이 그래프에 고정되어 최적화가 어려워집니다.
해결
register_buffer
, ONNX: ConstantInitializer 방식으로 구현적용 효과
[예시 3.6.3] Key/Value Caching (Decoder-only Transformer)
문제
decoder-only 구조에서는 autoregressive decoding 시 매 step마다 전체 context의 K/V를 다시 계산합니다.
sequence 길이가 늘어날수록 연산량이 선형 증가하고, inference latency가 크게 증가합니다.
해결
kv_cache
로 저장하고, query만 새로 계산 q_t × [K_1~K_t]
로 attention 수행 past_key_values
, TensorRT: set_kv_cache()
, ONNX: external buffer 연동 방식 활용적용 효과
[예시 3.6.4] Flash Attention 및 기타 구조 특화 최적화
문제
기존 attention은 q × k^T, softmax, v와의 곱을 각각 독립적으로 처리하며,
중간 결과를 full precision으로 저장해 memory usage가 급격히 증가합니다.
특히 long sequence에서 softmax overflow나 underflow 발생 가능성도 있습니다.
해결
적용 효과
기타 구조 특화 최적화
이처럼 Transformer 구조에서 반복적으로 등장하는 연산 흐름에 대해 구조 수준의 병합 또는 사전 계산을 적용하면, 단순한 연산자 최적화보다 훨씬 큰 latency 개선 효과를 얻을 수 있습니다. 특히 real-time generation이나 long-context decoding이 필요한 환경에서는 필수적인 최적화 기법이라 할 수 있습니다.
지금까지 살펴본 대부분의 최적화는 모델을 export하거나 컴파일하는 시점에 정적으로 적용되는 방식입니다. 하지만 실제 inference 환경에서는 입력 길이, batch size, GPU 종류, 메모리 상황 등 다양한 변수에 따라 최적의 실행 경로가 달라질 수 있기 때문에, runtime 시점에 적용되는 동적 최적화도 매우 중요합니다.
이 섹션에서는 런타임 상황을 고려한 다섯 가지 주요 최적화 기법을 다룹니다.
개요
동일한 연산이라도 kernel 설정(예: block size, tiling 전략, unroll factor 등)에 따라 성능 차이가 크게 납니다. Kernel auto-tuning은 다양한 설정을 미리 실행해보고 가장 빠른 구성을 선택해 실행하는 방식입니다.
구현 방식
적용 조건
효과
개요
tensor shape이 같더라도 memory layout(NCHW vs. NHWC, row/column major 등)에 따라
cache hit rate, memory access 효율이 달라집니다.
runtime에 최적의 layout을 선택하거나 변환해 연산 성능을 높일 수 있습니다.
구현 방식
kLINEAR
, kCHW32
) preferred_layout
옵션을 통해 backend가 최적 layout 선택적용 조건
효과
개요
dynamic shape을 지원하는 모델에서는 자주 등장하는 입력 shape에 대해
별도의 실행 계획을 생성해 캐시하고 재사용할 수 있습니다.
동적으로 들어오는 입력에 대해서도 static graph 수준의 성능을 확보할 수 있는 전략입니다.
구현 방식
적용 조건
효과
개요
연산자의 scheduling, memory allocation, kernel launch 순서 등을 runtime 최초 실행 시 미리 생성하고
동일 조건에서는 해당 계획을 재사용하는 방식입니다.
구현 방식
적용 조건
효과
개요
FP16, INT8 같은 low-precision 연산에서 overflow, NaN 등의 문제가 발생할 경우,
자동으로 FP32 연산으로 전환해 안정성을 확보하는 방식입니다.
구현 방식
적용 조건
효과
runtime-aware 최적화는 개발자의 명시적 설정 없이도, 환경과 입력 조건에 따라 모델이 자동으로 더 빠르고 안정적으로 동작하도록 만드는 기반입니다. 특히 production 환경이나 API inference에서 latency와 안정성이 중요한 경우 이러한 최적화는 성능 튜닝의 핵심 도구가 될 수 있습니다.
[예시 3.7.1] Kernel Auto-Tuning (TensorRT builder)
문제
같은 연산(MatMul, LayerNorm 등)이라도 block size, tiling 방식, memory layout 등에 따라 성능이 크게 달라질 수 있습니다.
예를 들어 (32, 1024) × (1024, 4096)
크기의 MatMul 연산에서는 block 크기 설정에 따라 최대 2배 이상 성능 차이가 발생합니다.
해결
TensorRT는 builder 단계에서 다양한 kernel configuration을 생성하고, micro-benchmark를 통해 가장 빠른 설정을 선택합니다.
선택된 kernel 경로는 serialized engine에 포함되어 이후 실행 시 재활용됩니다.
구현 방식
setMaxWorkspaceSize
, setFlag(kENABLE_TACTIC_HEURISTIC)
등 설정 적용 효과
[예시 3.7.2] Memory Layout Selection (ONNXRuntime + TensorRT)
문제
같은 tensor shape이더라도 memory layout(NCHW vs. NHWC, row-major vs. column-major)에 따라 성능이 달라집니다.
layout이 연산에 맞지 않으면 transpose가 삽입되어 memory copy와 latency overhead가 발생합니다.
해결
kLINEAR
, kCHW32
등)을 자동 선택 구현 방식
kPREFER_PRECISION_CONSISTENT_LAYOUT
설정 적용 효과
주의사항
[예시 3.7.3] Dynamic Shape Specialization (TensorRT Optimization Profile)
문제
Transformer 모델은 sequence length나 batch size가 유동적인 경우가 많습니다.
이를 dynamic shape으로 처리하면 shape 해석과 memory planning 때문에 runtime overhead가 발생합니다.
해결
자주 등장하는 shape에 대해 미리 여러 개의 optimization profile을 정의하고,
runtime에 해당 shape에 맞는 profile을 선택해 실행합니다.
구현 방식
create_optimization_profile()
→ min/opt/max shape 등록 ORT_ENABLE_PREPACK
설정으로 shape-specific plan 캐시 적용 효과
주의사항
[예시 3.7.4] Execution Plan Caching (ONNXRuntime SessionGraph)
문제
ONNX 모델 최초 실행 시 graph 파싱, 연산자 스케줄링, memory plan 수립, kernel 할당 등의 초기화가 필요합니다.
이 과정은 shape와 device config에 따라 달라지며 cold start latency가 수백 ms까지 증가할 수 있습니다.
해결
한 번 생성된 execution plan을 cache로 저장하고, 동일한 조건에서 그대로 재사용합니다.
구현 방식
enable_mem_pattern
, enable_cpu_mem_arena
) 적용 효과
주의사항
[예시 3.7.5] Fallback / Precision Promotion (Runtime Stability)
문제
INT8 또는 FP16 환경에서는 일부 연산(LayerNorm, Softmax 등)에서 underflow, overflow, NaN이 발생할 수 있습니다.
특히 Transformer 계열 모델은 정밀도 손실에 민감한 연산이 많습니다.
해결
runtime 시점에서 위험한 연산만 자동으로 FP32로 승격하거나, 해당 연산을 다른 backend로 fallback 처리합니다.
구현 방식
kSTRICT_TYPES
해제 시 fallback 허용 @custom_fp32_op
decorator로 selective promotion 지정 가능적용 효과
주의사항
이번 글에서는 실시간 RAG 시스템에서 발생하는 추론 병목 현상을 해결하기 위한 핵심 전략으로 ‘Computation Graph Optimization’의 필요성과 주요 기법들을 살펴봤습니다.
Operator Fusion, Constant Folding, Dead Code Elimination 등 operator-level 최적화부터, Dataflow-level과 Memory 최적화까지 각 단계별 접근법을 설명하며, Transformer 모델 특화 구조에 대한 인사이트도 함께 제공했습니다.
2부에서는 TorchScript, ONNX, TensorRT 등 대표적인 최적화 프레임워크를 비교 분석하고, 대규모 실험 결과를 기반으로 latency를 획기적으로 줄인 실제 사례를 공유합니다.
(2부 바로각: Computation Graph Optimization 2부 - 모델 추론 속도를 위한 그래프 구조 정리법