《GPU Mode》
L011
2024 · MAR · 16
High priority
transcript · available
Sparsity
“weight 의 절반이 0 이라면 곱하기를 절반만 해도 되지 않는가?” — 이 단순한 질문이 실제 GPU 위에서 빨라지려면 어떤 형식의 sparsity 가 어떤 hardware 위에서 의미를 가져야 하는지. Meta 의 Jesse Cai 가 — 2:4 structured sparsity가 Tensor Core 위에서 왜 1.6× speedup 으로 떨어지는지, sparsity 와 quantization 을 어떻게 함께 fuse 할 수 있는지를 깐다.
2:4 structured sparsity
Tensor Core
Sparse Tensor Core (SpTC)
cuSPARSELt
CUTLASS sparse GEMM
block sparsity
activation sparsity
torch.ao.sparsity
int8 + 2:4
J
Speaker
Jesse Cai
Meta · ao 팀 · sparsity + quantization
§ 01강의가 풀려는 문제· why this lecture exists
“weight 의 절반이 0” 이라는 사실은 GPU 에 그냥 안 통한다
Sparsity 의 직관은 단순하다 — weight 행렬에서 절반이 0 이면 곱하기를 절반만 해도 같은 결과가 나온다. 그런데 이 직관이 실제 GPU 위에서 1.6× 이상 빨라지려면 hardware 의 어떤 자리를 건드려야 하는지 — 이게 강의의 본론이다.
Jesse 가 강의에서 명시적으로 깔고 가는 사실 두 가지.
- unstructured sparsity 는 GPU 에서 거의 안 빠진다 — “0 자리는 곱하기 건너뛰자” 가 SIMT 모델에서는 분기를 만들고, coalesced load 를 깨고, Tensor Core 의 dense MMA 와 호환되지 않는다. CPU 에서는 통하지만 GPU 에서는 dense GEMM 보다 느려지는 경우가 많다.
- 그래서 NVIDIA 가 hardware 에 만들어 박은 형태가 “2:4 structured sparsity” — 4 개의 인접한 weight 중 정확히 2 개를 0 으로 강제. 이 패턴 위에서만 Sparse Tensor Core (Ampere 이후) 가 dense MMA 의 절반 시간으로 같은 일을 한다.
강의의 인지적 frame
이 강의는 “sparsity 가 좋다” 의 일반론이 아니라 — “2:4 structured sparsity 가 SpTC 위에서 어떻게 구체적으로 빨라지는가” 와 “PyTorch 의 torch.ao.sparsity 가 그걸 어떻게 노출하는가” 의 두 축으로 정확히 좁혀져 있다. Jesse 의 일관된 메시지: “hardware 가 노린 패턴 외에는 sparsity 의 이론적 이득이 코드에 안 떨어진다.”
“sparsity 의 1.6× speedup 은 hardware 가 약속한 2× 의 80% 다 — 그 20% 가 어디로 갔는지가 이 강의의 진짜 주제.”학습 노트 paraphrase
§ 02unstructured 가 GPU 에서 안 빠지는 이유· SIMT · coalesced load
“0 자리 건너뛰기” 는 분기를 만든다 — 그게 끝이다
전통적인 unstructured (random) sparsity — weight 행렬 안 임의의 자리에 0 이 흩어져 있는 형태 — 가 왜 GPU 에서 안 빠지는지부터 정리한다. 이걸 모르면 왜 NVIDIA 가 굳이 “2:4” 라는 이상한 제약을 만들었는지 이해가 안 된다.
이유 세 가지.
- 분기 (warp divergence) — 32 개 thread 가 한 warp 안에서 같은 instruction 을 도는데, “이 자리는 0 이니 건너뛰자” 가 thread 별로 다른 분기를 만들면 warp 가 직렬화된다. 이론상 절약된 곱셈만큼 시간이 안 줄어든다.
- coalesced load 가 깨진다 — GPU 의 HBM bandwidth 는 인접한 thread 가 인접한 주소를 읽을 때만 풀로 나온다. 0 이 흩어져 있으면 “0 아닌 자리만” 모으기 위해 indirect access 가 필요해지고, 같은 cache line 에 도달하지 못한다.
- Tensor Core 가 못 쓴다 — 가장 결정적. Tensor Core 는 4×4, 8×8, 16×16 같은 dense MMA tile 단위로만 작동한다. 그 안에 0 이 섞여 있어도 곱셈은 그냥 한다. instruction 의 의미가 그렇다.
FIG · unstructured 의 곱셈 자리0 도 그냥 곱한다
0
·
0
·
·
0
·
0
·
0
·
0
0
·
0
·
0
·
·
0
·
0
·
·
·
0
0
·
0
·
0
·
50% sparsity 인데 — Tensor Core 입장에서는 그저 “4×8 dense tile” 이다. 0 자리도 곱하기 instruction 이 발행된다. 이론상 50% 절약이 hardware 위에서는 0% 가 된다.
그래서 “sparse kernel 을 잘 짜기” 는 GPU 위에서 거의 항상 실패해왔다. 학계에서는 BSR (Block Sparse Row), CSR 같은 format 으로 시도했지만 — 작은 batch 에서는 dense MMA 보다 느린 경우가 대부분이다. 이 한계를 깨려고 hardware 자체에 새 instruction 을 박은 게 Ampere 의 Sparse Tensor Core.
§ 032:4 structured 의 발명· Ampere SpTC
“4 개 중 정확히 2 개만 0 이어야 한다” — 제약이 곧 hardware 가 약속하는 자리
Ampere 세대 (A100, RTX 30xx) 부터 들어온 Sparse Tensor Core (SpTC) 가 작동하는 조건은 딱 한 가지 패턴이다. weight 행렬을 4-element 단위 group 으로 나눴을 때, 각 group 마다 정확히 2 개의 element 만 non-zero. 임의 위치는 안 된다 — 4개 중 어느 2개를 살리는지는 자유지만, 4개 중 2개라는 비율은 fixed.
FIG · 2:4 structured pattern4-element group · 2 nonzeros
a
b
0
0
0
c
d
0
e
0
0
f
0
0
g
h
i
0
j
0
0
k
0
l
m
n
0
0
0
o
p
0
각 행이 4-element group 이고, 행마다 정확히 2 개의 non-zero. 위치는 group 마다 다를 수 있다. “어느 자리에 nonzero 가 있는지” 는 2-bit metadata 로 표현한다 — 4중 2 = C(4,2) = 6 가지지만 hardware 는 일부 layout 만 허용 (보통 4 가지).
왜 “2:4” 인가
NVIDIA 가 4 라는 group 크기를 고른 이유는 — Tensor Core 의 MMA instruction 이 다루는 가장 작은 reduction 축 단위가 16 element (FP16 의 경우) 이고, 그걸 4 개씩 나누면 group metadata 가 thread 1 개당 16-bit 로 깨끗이 떨어지는 자리가 나오기 때문. 즉 hardware 의 register 폭과 MMA 의 모양이 제약을 만들었다. 임의의 N:M 이 아니라 hardware 가 결정한 모양.
“2:4 sparsity 는 algorithm 이 아니라 instruction 이다 — cusparseLtMatmul 이 부르는 PTX 가 이 pattern 을 직접 안다.”강의 paraphrase
Hopper (H100) 세대에서는 같은 메커니즘이 더 정교해진다 — sparse MMA 가 FP8 까지 확장되고, throughput 의 약속이 dense 대비 더 명시적이다. (자세한 H100 의 sparse 매트릭스 instruction 변화는 NVIDIA arch whitepaper 직접 확인 필요.)
§ 042:4 의 storage layout· data + 2-bit metadata
data 절반 + 2-bit index — 메타데이터 비용을 어디서 회수하는가
2:4 sparsity 의 storage 는 두 부분이다. (1) compressed data — 원본의 nonzero 만 모은 것 (절반 크기), (2) metadata (indices) — group 마다 2 개의 nonzero 가 4 자리 중 어디 있었는지 표시 (group 당 2-bit, 총 12.5% overhead).
# 원본 weight (4 element group)
W = [a, b, 0, 0,
0, c, d, 0,
e, 0, 0, f]
# compressed (절반 크기)
W_c = [a, b,
c, d,
e, f]
# metadata — group 당 2-bit×2 = 4-bit
M = [0b0001, # a→pos 0, b→pos 1
0b0110, # c→pos 1, d→pos 2
0b1100] # e→pos 0, f→pos 3
실제 메모리 footprint 비교 — fp16 weight 의 경우.
- dense fp16 — element 당 16-bit. 100% baseline.
- 2:4 sparse fp16 — data 50% + metadata ~3% = 약 53%. 거의 절반.
- 2:4 sparse int8 — data 25% (int8 는 fp16 의 절반) + metadata 1.5% = 약 26.5%.
memory bandwidth 가 병목인 LLM inference (batch 1, autoregressive) 에서는 — HBM read 가 약 절반이라는 사실이 그대로 latency 절반으로 떨어질 가능성이 있다. 그게 1.6× speedup 의 핵심 메커니즘.
decode 비용은 어디로 갔는가
metadata 를 읽고 nonzero 자리를 복원하는 비용은 — SpTC 의 instruction 안에 hardware 가 흡수했다. PTX 의 mma.sp 명령이 metadata 를 직접 받아 dense × sparse MMA 를 수행. SW 로 풀려고 하면 항상 dense 보다 느려지지만 hardware 가 풀면 dense 의 절반.
§ 05실측 1.6×· memory bound
“이론상 2× 인데 왜 1.6× 인가” — 그 0.4 가 어디로 갔는가
Jesse 가 강의에서 가장 자주 인용한 수치 — “2:4 sparse GEMM 이 dense GEMM 대비 약 1.6× 빠르다”. NVIDIA 의 marketing 은 2× 라고 말하지만 실제 kernel benchmark 에서는 1.6×. 그 0.4 가 어디로 갔는지가 실용적인 디테일이다.
FIG · 2:4 vs dense GEMM 의 timing 분해BF16 weight, A100
Dense BF16 GEMM
100% (baseline)
1600 µs
이론상 2:4 절반
50% (이상적)
800 µs
실측 2:4 SpTC
62.5% (실측)
1000 µs
실측 1.6× 의 자리는 — metadata load + epilogue overhead + tail effect. (정확한 µs 수치는 강의 paraphrase. cuSPARSELt benchmark 직접 확인 필요.)
0.4× 가 어디로 갔는지의 분해.
- metadata load (~5-10%) — 추가 12.5% bandwidth. 하지만 cache 친화적이라서 실측은 5-10% 수준.
- epilogue 가 sparse-aware 가 아닐 때 (~10%) — bias add, activation 등이 dense 형태로 도는데 sparse output 과 join 할 때 형식 변환 비용.
- 작은 M 의 tail effect (~10%) — sparse GEMM 도 dense 와 같은 wave/tail 효과를 가진다. 작은 batch 에서는 wave 한 개의 latency 가 더 보이게 된다.
large batch vs small batch
1.6× 는 large M (training 또는 large batch inference) 의 수치. small M (batch 1, autoregressive decode) 에서는 memory-bound 가 강해지면서 더 큰 speedup 이 나오는 경우가 있다 — 이론상 2× 에 가까워진다. 단 latency variance 도 같이 커진다.
검증 방법
자기 모델에서의 실측은 torch.ao.sparsity.SparseSemiStructuredTensor 를 적용한 후 torch.profiler 로 GEMM kernel 만 따로 잰다. 다른 layer 가 끼면 1.6× 가 희석된다.
§ 06block sparsity· N:M 일반화
4 element group 보다 큰 알갱이 — 학계의 N:M 과 실용의 block sparse
2:4 외의 sparsity 형태들 — Jesse 가 빠르게 짚고 가는 자리. 강의 본론은 2:4 지만 다른 형식의 trade-off 를 같이 알아야 “왜 2:4 가 sweet spot 인가” 가 보인다.
N:M structured (일반화)
- N:M 은 “M 개 중 N 개만 nonzero” 의 일반형.
- 1:4, 2:8 같은 더 강한 sparsity 도 학계 논문에 나오지만 — 현재 hardware 는 2:4 만 지원한다 (Ampere/Hopper).
- 1:4 가 hardware 에 들어오면 4× speedup 가능성 — 다음 세대 GPU 에서 가능.
- 확인 필요: Blackwell 의 sparse MMA 변화.
block sparsity (큰 tile)
- 16×16, 32×32 같은 tile 단위로 0/1. tile 이 살아있으면 dense MMA, 죽어있으면 통째로 skip.
- 장점 — coalesced load 와 dense MMA 와 잘 맞는다.
- 단점 — 큰 알갱이라서 accuracy 회복이 더 어렵다. 보통 학습이 큰 fine-tuning 으로 회복해야 함.
- 실용 사례 — Mixture of Experts (MoE), SparseGPT 등.
activation sparsity
- weight 가 아니라 activation (feature map) 자체가 sparse 한 경우. ReLU 가 자연스럽게 만든다.
- 문제 — activation 의 sparsity pattern 은 매 input 마다 다르다. compile-time 에 fix 안 됨 → SpTC 활용 불가.
- 해결책 — top-k pruning 으로 동적으로 2:4 형태를 강제 (DejaVu, MoEfication).
왜 2:4 가 sweet spot
- (1) hardware 가 직접 instruction 으로 지원, (2) accuracy 회복이 거의 lossless 가능 (50% 만 잘라내면 됨), (3) 기존 dense weight 위에 mask + retrain 으로 변환 가능.
- 1.6× 가 실제 production 에 떨어지는 유일한 sparsity 형식 (현재 시점).
§ 07sparsity + int8 quant 의 fusion 함정· epilogue · cuSPARSELt
2:4 와 int8 을 합치면 4× 가 나와야 하는데 — 실제로는 어렵다
강의의 가장 실전적인 부분. sparsity 와 quantization 은 직교하는 최적화처럼 보이고, 둘을 같이 적용하면 “2× × 2× = 4×” 가 나와야 할 것 같다. 실제로는 fusion 의 함정에 자주 걸린다 — Jesse 가 자기 팀에서 직접 만난 사례.
문제의 자리 — cuSPARSELt 의 fusion 한계.
- cuSPARSELt 은 sparse GEMM 을 직접 수행하지만, 임의의 epilogue 를 fuse 하지는 못한다. 표준 epilogue (bias, ReLU) 외의 사용자 정의 op 를 못 끼운다.
- quantization 의 dequantize 단계 (int8 → fp16) 는 epilogue 안에서 일어나야 가장 빠른데, cuSPARSELt 가 그 형식의 fusion 을 안 받는다.
- 결과: sparse GEMM (cuSPARSELt) → 중간 buffer → dequant kernel 두 개로 갈라진다. HBM 왕복이 한 번 더 생긴다.
이 패턴이 “sparsity 이론 2× + quant 이론 2× 인데 실측 2.5×” 가 나오는 자리. 4× 가 안 나오는 진짜 이유.
CUTLASS sparse GEMM 의 등장
해결책 — CUTLASS 의 sparse GEMM template 위에서 직접 epilogue 를 짜는 것. cuSPARSELt 가 black box 라면, CUTLASS 는 author level (§ L010 의 분류) 에서 직접 fusion 을 정의할 수 있다. Jesse 의 팀이 이 길로 갔다 — 실제 측정으로 1600 µs → 약 600 µs (강의의 그래프).
학습 메모
이 자리는 단순한 “sparsity 적용” 강의가 아니라 — “두 최적화를 합치려면 둘 모두를 바꿀 수 있어야 한다” 는 시스템 디자인 메시지. cuSPARSELt 같은 high-level API 가 fusion 을 가두면 결국 CUTLASS 같은 더 낮은 layer 로 내려갈 수밖에 없다.
“sparse + quant 의 결합은 hardware 가 시킨 게 아니라 사람이 짠 거다 — 둘이 하나의 kernel 안에서 돌아야 한다.”Jesse Cai · 강의 paraphrase
§ 08PyTorch 통합· torch.ao.sparsity
SemiStructuredSparseTensor — 사용자 코드 두 줄
실제로 LLM 에 적용하려는 사용자 입장에서 — Jesse 의 팀이 만든 PyTorch 통합 (torch.ao.sparsity 와 SparseSemiStructuredTensor) 이 어떻게 보이는지. 코드 두 줄로 끝난다.
from torch.sparse import to_sparse_semi_structured
# dense → 2:4 sparse 로 변환
linear = nn.Linear(4096, 4096).cuda().half()
mask = create_2_4_mask(linear.weight) # Hessian-based 등
linear.weight = nn.Parameter(
to_sparse_semi_structured(linear.weight * mask)
)
# 이제 forward 가 cuSPARSELt 로 떨어진다
y = linear(x)
내부에서 일어나는 일.
to_sparse_semi_structured 가 compressed data + 2-bit metadata 형식으로 weight 를 재배치.
- 새 weight 는
SparseSemiStructuredTensor 클래스의 인스턴스 — __torch_dispatch__ 로 GEMM 호출이 가로채인다.
- 가로챈 GEMM 은 cuSPARSELt 또는 CUTLASS sparse template 으로 dispatch.
- 호출자(
linear(x)) 코드는 한 글자도 안 바뀐다.
이게 § L010 의 “user 가 손을 안 댄다” 디자인 원칙의 PyTorch 버전. 같은 user 코드 위에 sparsity 가 켜진다 / 꺼진다.
통합의 함정 — torch.compile 과의 호환
Jesse 가 강의에서 자주 짚은 부분 — torch.compile 의 Inductor 가 sparse tensor 를 항상 잘 다루지는 않는다. 특히 fusion graph 가 sparse 와 dense 사이를 오갈 때 graph break 가 발생할 수 있다. 해결: sparse layer 를 @torch.compile(dynamic=False) 와 함께 쓰거나, sparse 부분을 별도 module 로 격리.
§ 09accuracy 와 fine-tuning 회복· 2:4 mask 학습
“절반을 그냥 0 으로” 하면 손실은 얼마인가 — 그리고 어떻게 회복하는가
Sparsity 의 hardware 부분은 위에서 정리. 마지막 자리는 — “model 의 절반 weight 를 잘라내고 정확도를 어떻게 보존할 것인가”. 이게 잘 안 풀리면 1.6× speedup 이 의미가 없다.
가장 단순한 baseline — magnitude pruning. 각 4-element group 안에서 absolute value 가 작은 2 개를 0 으로. 이 방식만으로:
- BERT-base 같은 작은 모델은 1-2% 정확도 하락 정도 — 종종 finetune 없이 받아들임.
- 큰 LLM 은 더 많이 떨어진다. 5-10% perplexity 상승이 흔함. fine-tuning 이 거의 항상 필요.
회복 방법 — 2:4 mask 를 fix 하고 weight 를 재학습. mask 는 학습 동안 변하지 않게 하고 (constant boolean), weight 의 nonzero 자리만 update. 일반적으로 데이터의 1-5% 만으로 회복 가능 (downstream task 에 따라).
SparseGPT / Wanda — 더 정교한 pruning
Hessian 정보를 사용해 “이 weight 를 잘라내면 다른 weight 를 어떻게 보정해야 하는가” 를 푸는 방식 — SparseGPT (Frantar & Alistarh 2023), Wanda (Sun et al 2023). 큰 LLM 에서도 거의 lossless 한 2:4 sparsification 가능. 강의에서는 이름만 언급, 디테일은 별도 자료.
실용 권고
Jesse 의 권고 — 새 모델에 sparsity 를 적용하려면 (1) magnitude pruning 으로 baseline, (2) 5% 학습 데이터로 mask-frozen finetune, (3) accuracy delta 측정 후 진행 결정. 이 3단계가 1주일 안에 끝나는지가 production 적용 결정의 분기점.
“sparsity 의 ML 비용은 2:4 라는 hardware 모양에 model 을 맞추는 작업이다 — model 이 결정한 게 아니라 instruction 이 결정했다.”학습 노트 paraphrase
§ 10기억할 메모와 코드· key takeaways · repo
다시 열었을 때 5분 안에 손에 잡혀야 할 것
sparsity 를 다음에 마주쳤을 때 — “이게 hardware 위에서 의미가 있는가” 의 즉답을 줄 수 있는 핵심 메모.
unstructured ≠ fast
임의 위치의 0 은 GPU 에서 안 빠진다. SIMT 분기 + coalesced load 깨짐 + Tensor Core 와 호환 안 됨.
2:4 = hardware instruction
4 element group 마다 정확히 2 nonzero. Ampere SpTC (mma.sp) 의 instruction 모양이 결정한 제약.
실측 1.6× speedup
이론 2× 에 가까운 BF16 GEMM 의 실측. 0.4 가 사라지는 자리: metadata load + epilogue + tail effect.
storage 50% + 3% meta
compressed data 는 절반, 2-bit metadata 가 group 당 추가. memory bandwidth bound LLM 에서 거의 절반 latency.
cuSPARSELt vs CUTLASS
cuSPARSELt 은 black box, fusion 한계. quant + sparse 를 합치려면 CUTLASS sparse GEMM template 위에서 epilogue 직접 작성.
torch.ao.sparsity
to_sparse_semi_structured 두 줄로 dense weight 를 sparse 로. SparseSemiStructuredTensor 가 GEMM 호출 가로채서 cuSPARSELt 로 dispatch.
accuracy 회복
magnitude pruning + 5% data finetune 이 baseline. 큰 LLM 은 SparseGPT/Wanda 같은 Hessian-aware 방법 사용.
activation sparsity 한계
매 input 마다 pattern 이 다르다 → compile-time fix 안됨 → SpTC 활용 어려움. top-k 동적 강제로 우회.
손에 새기기 — 실습 시퀀스
- baseline 측정 — fp16 Linear 4096×4096 의 단일 GEMM 을
torch.profiler 로 측정. dense 의 µs 베이스라인.
- 2:4 변환 + 측정 —
to_sparse_semi_structured 로 같은 weight 를 sparse 로 변환 후 측정. 1.6× 가 자기 GPU 에서 실제로 나오는지 확인.
- metadata 직접 들여다보기 —
SparseSemiStructuredTensor 의 internal storage 를 print. data + indices 의 layout 이 § 04 와 같은지 확인.
- magnitude pruning 한 후 accuracy 측정 — BERT 또는 작은 LLM 의 한 layer 를 2:4 로 잘라내고 downstream task perplexity 측정. finetune 없이 얼마나 떨어지는가.
- cuSPARSELt 의 epilogue 한계 직접 만나기 — bias + GELU 같은 표준 epilogue 는 fuse 되는지, custom op 는 어디서 break 가 나는지 nsys 로 직접 확인.
- int8 + 2:4 시도 —
torchao 의 quantize_ 와 sparsity 를 같이 적용. 두 최적화의 곱이 실제 측정에서 얼마나 나오는지.
- SparseGPT 또는 Wanda 한 layer 적용 — magnitude pruning 보다 얼마나 적게 떨어지는지. Hessian 정보의 가치.
§ 11다른 강의로 이어지는 길· connections
이 강의의 도구가 다음에 어디에 다시 등장하는지
L011 의 sparsity 시각이 다른 강의의 quantization, fusion, kernel 시각과 어떻게 맞물리는지.
§ 12열린 질문· open questions
다음에 다시 들었을 때 직접 검증해야 할 것들
sparsity 의 hardware 디테일이 GPU 세대마다 변하기 때문에 — 이 강의(2024 March) 시점 이후 자료의 갱신이 필요한 자리들.
- Hopper / Blackwell 의 sparse MMA 변화 — H100 의 FP8 sparse, B200 의 새 instruction. 강의는 Ampere 위주. NVIDIA arch whitepaper 직접 확인 필요.
- 1.6× 의 정확한 분해 — § 05 의 metadata 5-10%, epilogue 10%, tail 10% 는 패턴 추정치. cuSPARSELt benchmark 의 정확한 수치 확인 필요.
- activation sparsity + 2:4 의 fusion — DejaVu / MoEfication 같은 동적 sparsity 가 SpTC 위에서 실제로 도는지, hardware 적합성 검증 필요.
- cuSPARSELt 의 최신 epilogue 지원 범위 — 강의 시점 이후 cuSPARSELt 가 더 많은 epilogue 를 지원했을 가능성. NVIDIA docs 확인.
- sparse + FP8 — Hopper 에서 sparse + FP8 의 production-grade 사용처 (있는지 + 어디서 빠지는지).
- SparseGPT vs Wanda 의 trade-off — 강의에서 이름만 언급. 실제 LLM 에서 둘의 정확도 vs 시간 비용 비교 필요.
검증 메모
이 노트의 모든 timing 수치 (1600 µs, 1000 µs 등) 는 강의의 그래프를 paraphrase 한 형태이며, 자기 GPU + cuSPARSELt 버전에서 직접 측정해야 본인의 baseline 을 안다. torch.utils.benchmark 와 torch.profiler 로 한 번 직접 재보는 것이 강의 학습의 마무리.