gpumode · 강의 아카이브
《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
강의 번호
L011
스피커
Jesse Cai
학습 우선순위
High · 정독
Slides
repo · sparsity.pptx
§ 01강의가 풀려는 문제· why this lecture exists

“weight 의 절반이 0” 이라는 사실은 GPU 에 그냥 안 통한다

Sparsity 의 직관은 단순하다 — weight 행렬에서 절반이 0 이면 곱하기를 절반만 해도 같은 결과가 나온다. 그런데 이 직관이 실제 GPU 위에서 1.6× 이상 빨라지려면 hardware 의 어떤 자리를 건드려야 하는지 — 이게 강의의 본론이다.

Jesse 가 강의에서 명시적으로 깔고 가는 사실 두 가지.

강의의 인지적 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” 라는 이상한 제약을 만들었는지 이해가 안 된다.

이유 세 가지.

  1. 분기 (warp divergence) — 32 개 thread 가 한 warp 안에서 같은 instruction 을 도는데, “이 자리는 0 이니 건너뛰자” 가 thread 별로 다른 분기를 만들면 warp 가 직렬화된다. 이론상 절약된 곱셈만큼 시간이 안 줄어든다.
  2. coalesced load 가 깨진다 — GPU 의 HBM bandwidth 는 인접한 thread 가 인접한 주소를 읽을 때만 풀로 나온다. 0 이 흩어져 있으면 “0 아닌 자리만” 모으기 위해 indirect access 가 필요해지고, 같은 cache line 에 도달하지 못한다.
  3. 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
2:4 + int8
35%
560 µ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 한계.

  1. cuSPARSELt 은 sparse GEMM 을 직접 수행하지만, 임의의 epilogue 를 fuse 하지는 못한다. 표준 epilogue (bias, ReLU) 외의 사용자 정의 op 를 못 끼운다.
  2. quantization 의 dequantize 단계 (int8 → fp16) 는 epilogue 안에서 일어나야 가장 빠른데, cuSPARSELt 가 그 형식의 fusion 을 안 받는다.
  3. 결과: 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.sparsitySparseSemiStructuredTensor) 이 어떻게 보이는지. 코드 두 줄로 끝난다.

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)

내부에서 일어나는 일.

  1. to_sparse_semi_structuredcompressed data + 2-bit metadata 형식으로 weight 를 재배치.
  2. 새 weight 는 SparseSemiStructuredTensor 클래스의 인스턴스 — __torch_dispatch__ 로 GEMM 호출이 가로채인다.
  3. 가로챈 GEMM 은 cuSPARSELt 또는 CUTLASS sparse template 으로 dispatch.
  4. 호출자(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 동적 강제로 우회.

손에 새기기 — 실습 시퀀스

  1. baseline 측정 — fp16 Linear 4096×4096 의 단일 GEMM 을 torch.profiler 로 측정. dense 의 µs 베이스라인.
  2. 2:4 변환 + 측정to_sparse_semi_structured 로 같은 weight 를 sparse 로 변환 후 측정. 1.6× 가 자기 GPU 에서 실제로 나오는지 확인.
  3. metadata 직접 들여다보기SparseSemiStructuredTensor 의 internal storage 를 print. data + indices 의 layout 이 § 04 와 같은지 확인.
  4. magnitude pruning 한 후 accuracy 측정 — BERT 또는 작은 LLM 의 한 layer 를 2:4 로 잘라내고 downstream task perplexity 측정. finetune 없이 얼마나 떨어지는가.
  5. cuSPARSELt 의 epilogue 한계 직접 만나기 — bias + GELU 같은 표준 epilogue 는 fuse 되는지, custom op 는 어디서 break 가 나는지 nsys 로 직접 확인.
  6. int8 + 2:4 시도torchaoquantize_ 와 sparsity 를 같이 적용. 두 최적화의 곱이 실제 측정에서 얼마나 나오는지.
  7. SparseGPT 또는 Wanda 한 layer 적용 — magnitude pruning 보다 얼마나 적게 떨어지는지. Hessian 정보의 가치.
§ 11다른 강의로 이어지는 길· connections

이 강의의 도구가 다음에 어디에 다시 등장하는지

L011 의 sparsity 시각이 다른 강의의 quantization, fusion, kernel 시각과 어떻게 맞물리는지.

§ 12열린 질문· open questions

다음에 다시 들었을 때 직접 검증해야 할 것들

sparsity 의 hardware 디테일이 GPU 세대마다 변하기 때문에 — 이 강의(2024 March) 시점 이후 자료의 갱신이 필요한 자리들.

검증 메모

이 노트의 모든 timing 수치 (1600 µs, 1000 µs 등) 는 강의의 그래프를 paraphrase 한 형태이며, 자기 GPU + cuSPARSELt 버전에서 직접 측정해야 본인의 baseline 을 안다. torch.utils.benchmarktorch.profiler 로 한 번 직접 재보는 것이 강의 학습의 마무리.

← Lecture 010 Prod Ready CUDA Library — fusion 디자인의 일반론에서 specific sparsity 로 Lecture 012 → Flash Attention — Thomas Viehmann 이 깐 online softmax 와 tile streaming