《GPU Mode》
L007
2024 · FEB · 24
High priority
transcript · available
Advanced Quantization
GPT-Fast / SAM-Fast 의 “100 줄 미만 fast 한 LLM 추론” 의 비결 중 하나가 quantization. PyTorch 가 이 기능을 매우 fast 하게 (그리고 매우 적은 코드로) 갖게 된 비밀은 — algorithm 은 Python 으로, kernel 은 torch.compile 이 자동 Triton 으로 의 분리. Charles Hernandez 가 dynamic / weight-only / int4 / GPTQ 까지 가는 길에서 “CUDA 손코딩” 과 “Triton 자동 생성” 을 어디서 어떻게 갈아탔는지의 일기. “Triton 의 magical 함이 awesome 하면서도 frustrating 하다” 가 강의의 톤.
dynamic quantization
weight-only quant
int4 GPTQ
torch.compile
tinygemm
Tensor Core
CUDA vs Triton
torchao
C
Speaker
Charles Hernandez
Meta · PyTorch quantization · torchao 메인테이너
§ 01강의가 풀려는 문제· Why this lecture exists
“GPU 위 quantization 이 늦었던 이유” 를 history 로 풀어내기
강의의 첫 솔직한 한 마디. “GPU quantization 이 이때까지 없었던 이유는 — quantized GPU 커널이 없었기 때문이다.” CPU 진영에서는 quantization 이 흔했다 (oneDNN, FBGEMM). GPU 에서는 “fp16 이 충분히 빠르니까” 의 분위기. 그 가정이 깨진 게 LLM 시대 — 모델이 GPU 메모리에 안 들어가고, 추론의 bottleneck 이 바뀌었다.
강의가 답하는 세 질문.
- quantization 이 GPU 위에서 무엇을 푸는가 — bandwidth? compute? 둘 중 어느 쪽이 표적인가.
- algorithm 과 kernel 의 분업이 어떻게 진화했는가 — 이전엔 “양쪽 다 직접” 이 표준, 이제는 “algorithm Python + kernel torch.compile” 의 분리.
- CUDA 와 Triton 사이의 결정 — Charles 가 직접 헤맨 자리들에서의 교훈.
시리즈 안의 위치
이 강의는 GPU Mode 의 첫 “실전 LLM 추론” 영역. L001 의 도구, L004 의 정량, L006 의 fusion 패턴이 모두 한 자리에 모인다 — 그리고 algorithm 의 본격 자유도가 추가된다. 정확도와 속도의 trade-off 가 처음 등장.
“Triton 이 magical 하게 빠를 때 — 그 자체가 awesome 하면서도 frustrating 하다. 왜 빠른지를 모르면.”Charles Hernandez · 학습 노트
§ 02quant 의 두 표적· memory · compute
같은 기법이 어떤 목적인지에 따라 다르게 동작한다
quantization 의 motivation 은 두 갈래다 — ① memory 줄이기 (모델이 GPU 에 안 들어가니), ② compute 빠르게 (lower precision = 더 많은 FLOPs/sec, Tensor Core 활용). LLM 추론에서는 거의 항상 ① 이 dominant.
memory-bound 인 경우 — weight-only quant
- LLM decode 단계 (한 token 씩) 가 대표적
- arithmetic intensity 매우 낮음 (1 token × weight matrix)
- 표적: weight 의 메모리 — int8 또는 int4 로
- activation 은 fp16 그대로 — 정확도 거의 손실 없음
- matmul 시 weight 만 dequantize 후 fp16 matmul
- 가속: 약 2–4×
compute-bound 인 경우 — dynamic / static quant
- LLM prefill 단계 (큰 batch 또는 긴 prompt)
- activation 도 quantize → int 곱셈 → Tensor Core int8
- 표적: FLOPs throughput
- activation 의 동적 범위에 민감 → 정확도 관리
- scale/zero 계산 비용 추가
- 가속: 거의 linear 한 dtype 비례
강의에서 Charles 가 짚는 사실 — 대부분의 LLM 추론은 memory-bound. 그래서 이 강의의 본론은 weight-only quant 와 GPTQ. activation quant 는 prefill 가속이나 batch 가 큰 경우에만.
§ 03dynamic quantization· algorithm in Python, kernel by torch.compile
torch.compile 이 모든 quant kernel 을 자동으로 만들어 주는 첫 사례
강의의 가장 인상적인 한 줄. “dynamic quantization 의 모든 quant 부분을 그냥 Python 으로 짜고, torch.compile 이 알아서 다 한다.” 결과 — Triton 도 안 써도 되고, CUDA 도 안 써도 된다. 한 PyTorch 함수가 통째로 inductor 의 표적.
# dynamic quant 의 핵심 — Python 그대로
def dynamic_quantize_linear(x, weight_int8, weight_scale):
# activation 의 동적 범위로 scale 계산 (Python pointwise)
x_max = x.abs().amax(dim=-1, keepdim=True)
x_scale = x_max / 127.0
x_int8 = (x / x_scale).round().to(torch.int8)
# int8 matmul (Tensor Core int8 로 lower 됨)
out_int32 = torch._int_mm(x_int8, weight_int8)
# dequantize
return out_int32.to(torch.float16) * x_scale * weight_scale
# 한 줄 추가로 fast
fast = torch.compile(dynamic_quantize_linear)
이 코드의 모든 부분이 의미 있다.
- activation 의 quant 도 Python pointwise —
amax, round, to(int8) 가 평범한 PyTorch op.
- int matmul 만 native —
torch._int_mm 이 Tensor Core int8 instruction 으로 lower 됨.
- dequant 도 Python — output 을 fp16 으로 cast 하고 scale 곱하기.
torch.compile 이 모든 pointwise 를 fuse — Inductor 가 quant + dequant + scale multiply 를 한 두 개의 Triton 커널로.
왜 이게 “magical” 인가
Charles 의 직접적 표현 — “이전에는 같은 일을 하려면 quant 커널, dequant 커널, fused matmul 커널을 직접 짰다. 이제는 Python 으로 알고리즘만 짜면 끝.” torch.compile 의 fusion 능력이 quant 같은 “많은 작은 산술” 영역에서 가장 빛나는 자리.
강의에서 Charles 가 직접 제시한 시간 — fast 한 GPU 위에서 ~150 token/s (정확한 숫자는 변동, 본문에서 “limited GPU” 라 명시). 같은 알고리즘을 fp16 baseline 과 비교해 약 2–3× 가속.
§ 04weight-only quantization· int8 weight, fp16 activation
LLM 추론의 가장 흔한 형태 — 메모리만 줄이고 정확도 거의 보존
메모리 줄이기가 표적인 케이스. weight 만 int8 (또는 int4) 로 저장, activation 은 fp16 그대로. matmul 시점에 weight 를 dequantize 해서 fp16 matmul. 가속의 비결은 — HBM 에서 weight 를 절반/1/4 만 읽기.
offline
model 의 모든 linear weight 를 int8 로 quantizeper-channel scale 저장 — fp16 한 vector
한 번만
runtime · matmul
x (fp16) @ W (int8) 를 fused dequant + matmul 로한 커널 안에서 read int8 → cast fp16 → multiply scale → matmul
매 forward
decode 가속
x 가 (1, hidden) 같이 작을 때 — bandwidth 가 dominantweight read 가 1/2 → 시간 거의 절반
실측
정확도
per-channel scale 이면 INT8 weight 도 거의 losslessround-to-nearest 의 산술 오차 ~0.1% 정도
검증
이 패턴의 핵심 디테일 — per-channel quantization. 한 weight matrix 를 한 scale 로 quantize 하면 어떤 channel 의 큰 값이 작은 channel 의 정밀도를 망친다. 그래서 each output channel 에 한 scale (즉 한 column 의 fp16 vector) 를 저장.
torch.compile 이 같은 trick
weight-only quant 도 dynamic 처럼 Python 으로 알고리즘만 짜고 torch.compile. (W_int8.to(fp16) * scale) @ x 같은 코드를 fuse 해서 “read int8 → cast → multiply → matmul” 의 한 커널을 자동 생성. 별도 CUDA 코드 작성 안 함.
§ 05int4 와 tinygemm· block_size 의 함정
int8 → int4 로 한 단 더 — packing 과 block_size 의 까다로움
메모리를 더 줄이는 다음 단계가 int4. byte 안에 두 element 를 packing. 이 packing 의 layout 과 dequantize 가 까다로워 — int8 의 “Python 으로 그대로” 패턴이 그대로는 안 된다. tinygemm 같은 전용 CUDA 커널이 필요.
fp16
2 byte
baseline · default activation/weight
×1
int8
1 byte
memory 1/2. per-channel scale (fp16) · 정확도 거의 보존. torch.compile 만으로 충분
×2
int4
0.5 byte
memory 1/4. byte 안에 2 element packing · group quantization 필요. tinygemm 같은 전용 커널
×4
int2
0.25 byte
memory 1/8. 정확도가 GPTQ 같은 정교한 알고리즘 필요 · still research
×8
fp4 / nf4
0.5 byte
non-linear bin · QLoRA 의 표준 · int4 와 같은 메모리지만 정확도 ↑
×4
int4 quant 의 핵심 디테일 — group quantization. 한 row 또는 column 안에서도 element 들을 group 으로 묶고 group 마다 별도 scale. group_size = 32, 64, 128 가 흔함. 작을수록 정확하지만 scale buffer 가 커진다.
# int4 의 packing layout (단순화)
# 한 byte 가 두 int4 를 들고 있음
packed_byte = (high_4bit << 4) | (low_4bit & 0x0F)
# tinygemm 의 dequant — bit 연산 + scale
high = (packed_byte >> 4) - 8 # signed [-8, 7]
low = (packed_byte & 0x0F) - 8
val_high = high.to(fp16) * scales[group_h]
val_low = low.to(fp16) * scales[group_l]
왜 torch.compile 만으로 안 되는가
bit 연산과 group indexing 이 들어가면 — Inductor 가 만드는 코드의 quality 가 좋지 않을 수 있다. “Triton 도 bit 연산이 약간 까다롭다” 가 강의의 한 마디. 그래서 NVIDIA 의 tinygemm 같은 직접 CUDA 커널이 standard. PyTorch core 가 호출만 한다.
§ 06GPTQ — model agnostic 으로 다시· 왜 직접 구현했나
auto-gptq 가 있었지만 — 자기 quant kernel 과 “안 맞아서”
GPTQ 는 “weight 를 한 row 씩 양자화하면서 다음 row 가 quantization error 를 보정” 하는 알고리즘. 이미 auto-gptq 같은 library 가 있다. 그런데 Charles 와 팀이 직접 다시 구현한 이유 — auto-gptq 의 weight layout 이 자기 quant kernel (tinygemm 등) 과 맞지 않았다.
이 일화의 메시지는 미묘하다.
- algorithm 과 kernel 사이의 layout 계약 — quantization 알고리즘이 weight 를 어떤 모양으로 저장하는가가 그 다음 kernel 의 가능성을 정한다.
- “model agnostic” — auto-gptq 는 특정 모델 (GPT-2/GPT-J 등) 에 맞춰져 있어, 임의의 PyTorch model 에 일반적으로 적용하기 어려웠다.
- algorithm 자체는 Python 으로 다시 짤 수 있다 — quant 의 정확도 부분은 결국 작은 행렬 산술. CUDA 가 필요한 건 그 결과로 얻은 weight 의 fast matmul.
algorithm 의 분리
강의의 큰 메시지가 여기서 명확해진다 — quantization 알고리즘 (GPTQ, AWQ, SmoothQuant) 은 Python 으로 짜고, runtime kernel 은 분리해서 별도 최적화. algorithm 마다 새 kernel 을 짤 필요가 없다. 같은 weight layout 만 유지하면.
“같은 algorithm 이 여러 kernel 위에서 도느냐, 다른 algorithm 이 같은 kernel 위에서 도느냐 — 이게 quantization library 디자인의 첫 결정.”학습 노트 · L007 §06
§ 07CUDA 와 Triton 의 갈림길· 언제 어디서 갈아탔나
Charles 의 일기 — “여기서는 CUDA, 여기서는 Triton, 여기서는 torch.compile”
강의의 가장 실전적인 부분. Charles 가 quant 일을 하면서 어디에서 어떤 도구를 썼는지의 직접적인 회고.
dynamic quant
activation 의 scale 도 매 forward 다시 계산algorithm 이 단순한 pointwise 의 chain — torch.compile 이 완벽
torch.compile
int8 weight-only
weight 만 quantize 된 채로 fp16 matmul여기서도 torch.compile 이 fused dequant + matmul 을 잘 만든다
torch.compile
int4 weight-only
packing 과 group_size 가 들어가니 자동 생성 코드 quality 가 떨어짐tinygemm 같은 전용 CUDA kernel
CUDA
FlashAttention-like
algorithm 자체가 tile 단위 + online softmaxTriton 으로 짜는 게 가장 자연스러움 — tile abstraction
Triton
A/B 테스트
Triton 으로 짠 quant 커널 vs torch.compile 의 자동 생성“최근 PyTorch 에 통합된 후 거의 같다” — 즉 Triton 이 거의 cuBLAS 수준
실측
이 회고가 시리즈의 큰 메시지를 압축한다.
- algorithm 이 단순한 pointwise chain → torch.compile. 코드 변경 없이 자동.
- tile 단위 매핑이 자연스러우면 → Triton. attention, normalization, softmax.
- bit packing, group indexing, asymmetric layout → 직접 CUDA. compiler 가 잘 못 한다.
강의의 직접 인용
“Triton 커널이 cuBLAS 같은 직접 CUDA 커널이랑 거의 같이 빠른 경우가 자주 있다.” 이 한 줄이 Triton 의 현재 상태에 대한 솔직한 평가. 큰 격차가 있던 옛날 (~2022) 과 달리, 2024 시점에서 production-quality.
§ 08block_size 와 launch shape· Triton 의 magical 함정
“Triton 이 magical 한 만큼 frustrating 한 자리”
강의에서 Charles 가 직접 디버깅 한 일화. quant Triton 커널이 어떤 사이즈에서 너무 빠르게 도는데, 다른 사이즈에서 갑자기 느려졌다. 원인 추적 — block_size 가 모델의 hidden dimension 을 limit 하고 있었다.
이 사실을 풀어 보면.
- Triton 의
BLOCK_SIZE 가 작으면 — SM 당 block 수가 많아 occupancy 가 좋다, 하지만 register 사용이 작아 ILP (instruction-level parallelism) 가 부족.
BLOCK_SIZE 가 크면 — register reuse 가 좋아 빠르지만, 어떤 hidden dim 에서는 마지막 block 이 비거나 한 SM 에 한 block 만 실행 가능 (occupancy ↓).
- 최적 BLOCK_SIZE 가 모델 사이즈에 의존 — 한 set 의 BLOCK_SIZE 로 모든 모델을 cover 할 수 없다. autotune 이 답.
강의의 hint
NCU 가 그 자리에서 “grid is too small to fill available resources” 같은 hint 를 줘서 — Charles 가 BLOCK_SIZE 를 키운 뒤 트렌드가 뒤집혔다 (L001 §06 의 Mark 의 동일한 일화). 같은 launch 설정 함정을 두 강의가 독립적으로 만난다는 사실 자체가 큰 메시지.
“소스 코드는 정확한데 launch 설정 한 줄로 10배 차이. magical 한 게 아니라 — 정량을 알면 보이는 trade-off.”Charles Hernandez · L007
§ 09production 의 거리· QAT · sensitivity · per-layer
“모든 layer 를 quantize” 가 답이 아닌 이유
강의 후반의 sober 한 메시지. weight-only int8 quant 만 하면 거의 정확도 손실 없음 — 그런데 conv, layernorm, embedding 등을 모두 quantize 하면 정확도가 떨어지는 일이 흔하다. production 까지 가려면 per-layer sensitivity 와 경우에 따라 QAT (quantization-aware training) 가 필요.
- per-layer sensitivity — 어떤 layer 가 quantization 에 민감한가의 측정. 한 layer 만 quantize 한 채 모델을 돌리고 정확도 측정 → 가장 둔감한 layer 부터.
- QAT — 학습 중에 fake quantize → dequantize 의 round 를 끼워넣어 모델이 quant 를 알고 학습. 학습 비용이 추가.
- SmoothQuant / AWQ — activation outlier 를 weight 로 옮기는 reparametrization. 정확도와 가속을 모두 잡는 알고리즘.
torchao 의 위치
강의에서 Charles 가 본격적으로 깔진 않지만 — torchao (PyTorch ao = architecture optimization) 가 이 모든 패턴의 통합 layer. dynamic, weight-only, int4, GPTQ, QAT, SmoothQuant 가 같은 API 위에. algorithm 은 자유, kernel 은 통일 의 디자인.
이 강의의 마지막 톤은 — “quantization 은 algorithm 과 systems 의 양쪽 영역. algorithm 이 정확도를 결정하고, systems 가 속도를 결정한다. 둘 다 손에 잡고 있어야 한다.” 이게 GPU Mode 에서 다음 quant 강의들 (L034 Low-bit Triton, AWQ 강의 등) 의 reading frame.
§ 10기억할 메모와 코드· key takeaways · repo
다시 열었을 때 5분 안에 손으로 잡혀야 할 것
quant 의 dtype 사다리, algorithm/kernel 분리, CUDA/Triton 갈림길의 핵심.
memory vs compute
LLM decode → memory-bound → weight-only int8/int4. prefill → compute → activation quant.
algorithm in Python
dynamic, weight-only int8 의 quant 부분은 Python pointwise. torch.compile 이 fused 커널 자동.
int8 vs int4
int8 → torch.compile. int4 → packing/group → tinygemm 같은 직접 CUDA.
per-channel scale
weight 의 column 마다 별도 fp16 scale. accuracy 의 핵심.
group quantization
int4 의 표준. group_size = 32~128. 작을수록 정확, 클수록 메모리 효율.
CUDA / Triton / compile 갈림길
pointwise → compile, tile → Triton, bit/packing → CUDA.
launch shape 함정
BLOCK_SIZE 가 작으면 ILP, 크면 occupancy. autotune 이 답.
algorithm/layout 계약
quantization algorithm 이 weight 를 어떤 layout 으로 저장하는가가 그 다음 kernel 의 가능성을 정한다.
production 의 거리
per-layer sensitivity · QAT · SmoothQuant — accuracy 보존을 위한 algorithm 영역.
손에 새기기 — 실습 시퀀스
- fp16 baseline — 작은 LLM (GPT-2 small 또는 LLaMA-1B) 의 한 forward 시간 측정. tokens/sec 기록.
- dynamic quant 적용 — 강의의 Python 코드 그대로.
torch.compile wrap. 시간 비교 — baseline 대비 가속률.
- weight-only int8 — torchao 의
quantize_(model, int8_weight_only) 또는 직접 구현. 정확도 (perplexity 또는 다른 metric) 가 baseline 과 거의 같은지 검증.
- int4 group quant — torchao 의
int4_weight_only. group_size sweep (32, 64, 128) 으로 정확도 vs 가속의 trade-off 측정.
- BLOCK_SIZE sweep — Triton quant 커널의 BLOCK_SIZE 를 변화시키면서 시간. 최적값이 모델 hidden dim 에 어떻게 의존하는가.
- per-layer sensitivity — 한 layer 만 fp16 으로 두고 나머지 quantize. 어느 layer 가 가장 민감한가 (보통 첫 layer 와 마지막 layer).
- chrome trace 비교 — fp16, int8 weight-only, int4 의 각 forward 의 trace 를 비교. matmul 막대의 두께와 메모리 트래픽 차이.
- 한 페이지 plan — 자기가 다루는 모델의 추론 latency 를 줄이려면 어떤 quant 가 답인지 — bound, 정확도 요구, 모델 사이즈를 기반으로 한 페이지로.
§ 11다른 강의로 이어지는 길· connections
이 강의의 패턴이 어디서 다시 등장하는지
algorithm/kernel 분리, CUDA/Triton/compile 갈림길의 단어들이 이후 GPU Mode quant 강의들의 기본 어휘.
§ 12열린 질문· open questions
다음에 다시 들었을 때 직접 검증해야 할 것들
강의에서 흐릿하게 지나간 자리들과, 자기 환경에서 직접 봐야 할 사실들.
- 정확한 가속 수치 — 강의에서 “limited GPU 에서 ~150 token/s” 같은 정성적 표현. 자기 GPU 에서 fp16 vs int8 vs int4 의 token/sec 를 직접 측정. (확인 필요)
- tinygemm 의 내부 — 강의에서 “전용 CUDA kernel” 이라고만. 정확한 layout (group_size, packing 순서) 와 PTX 수준의 instruction 사용은 별도 추적.
- Triton vs CUDA 의 정확한 격차 — Charles 가 “거의 같다” 고 했지만 정확한 비율은 강의에서 안 다룸. matmul / quant / attention 별로 다를 가능성.
- per-layer sensitivity 의 자동화 — 강의에서 일부 언급. 실제 sensitivity scan 의 표준 도구가 무엇인가? L034 또는 별도 quant 강의.
- SmoothQuant / AWQ / GPTQ 의 정확도 vs 가속 비교 — 강의에서 GPTQ 만 본격 다룸. 다른 algorithm 들의 trade-off 는 후속 강의의 영역.
- activation quantization 의 fp8 옵션 — Hopper (H100) 의 fp8 Tensor Core. int8 대신 fp8 의 production 사용은 강의 시점 (2024-02) 이후 본격화.
- QLoRA 와의 관계 — fine-tuning 시점의 quant 가 강의의 표적이 아님. PEFT 영역에서 별도.