gpumode · 강의 아카이브
《GPU Mode》 L015 2024 · APR · 13 High priority transcript · available

CUTLASS

CUTLASS 는 NVIDIA 의 GEMM/conv 를 위한 C++ template 라이브러리. “cuBLAS 의 black box 를 열어서 자기 epilogue 와 fusion 을 끼울 수 있는 자리”를 제공한다. Eric Auld 가 — CUTLASS 코드의 telltale sign (_ placeholder, CuTe layout, cute::tile) 부터 hierarchical tiling 의 사고방식까지 — 처음 보는 사람도 코드를 읽을 수 있게 깐다.

CUTLASS CuTe (Cute Tensor) layout 추상 Tensor Core MMA hierarchical tiling epilogue fusion cuBLAS vs CUTLASS underscore placeholder FlashAttention 의 base
E
Speaker
Eric Auld
CUTLASS · GEMM 코드의 reading guide
강의 번호
L015
스피커
Eric Auld
학습 우선순위
High · 정독
자료
repo 별도 폴더 없음
§ 01강의가 풀려는 문제· why this lecture exists

“cuBLAS 위에 한 줄을 끼우고 싶을 때 — 갈 곳이 없다”

cuBLAS, cuDNN 같은 NVIDIA 의 high-level library 는 GEMM/conv 를 비싸게 빠르게 한다. 단, black box 다. 사용자가 GEMM 의 끝에 quantize step 을 끼우거나, attention 의 score 를 받아 다른 op 를 fuse 하려고 하면 — host 로 한 번 나갔다 돌아와야 한다. HBM 왕복 한 번이 추가.

CUTLASS 가 그 자리를 푼다. 같은 GEMM kernel 의 “author” 입장에서 코드를 작성할 수 있게 — C++ template + GPU device code 의 라이브러리를 제공한다. 강의가 풀려는 두 가지.

  1. CUTLASS 코드를 처음 봐도 읽을 수 있게 — telltale sign 과 mental model 을 깐다. 코드의 _cute:: namespace 가 무슨 뜻인지.
  2. CuTe layout 추상이 hierarchical tiling 을 어떻게 표현하는가. shape × stride 의 일반화된 모양.
강의의 인지적 frame

이 강의는 “CUTLASS 로 GEMM 을 어떻게 짜는가” 의 tutorial 이 아니다 (그건 NVIDIA examples 가 한다). “이미 있는 CUTLASS 코드를 어떻게 읽는가” 의 reading guide — Eric 이 ML team 에서 “이 kernel 은 왜 안 빠른가” 를 답하는 사람의 시각으로 깐다.

“CUTLASS 는 black box 의 반대다 — 모든 게 보이지만, 그래서 뭐가 뭔지 알아야 한다.”Eric Auld · 강의 paraphrase
§ 023 단계 추상· host API · CUTLASS · CuTe

같은 GEMM 을 세 깊이로 보는 자리 — user 가 어디까지 내려가는가

Eric 의 frame — NVIDIA 의 GEMM stack 은 사실 3 layer. 사용자는 자기 task 에 맞는 layer 만 보면 된다.

L0 · host APIcuBLAS, cuDNN, cuSPARSELt

host 함수 한 줄 호출. cublasGemmEx(...). 가장 빠르지만 fusion 안 됨. 사용자가 짜는 코드는 host 측. NVIDIA 가 internal kernel 결정.

L1 · CUTLASSC++ template kernel

device kernel 자체를 사용자가 instantiate. epilogue 로 사용자 정의 후처리 fuse 가능. 이 강의의 자리. cuBLAS 의 ~95% 성능까지.

L2 · CuTelayout primitive

CUTLASS 안에서 가장 깊은 layer. tensor 의 layout 을 Layout<Shape, Stride> 로 표현하는 추상. CUTLASS 3.x 부터 standard. 가장 깊지만 가장 강력한 자리.

version note

CUTLASS 2.x 까지는 epilogue 가 별도 template 으로 따로 박혀 있었지만 — CUTLASS 3.x (Hopper 이후) 부터는 CuTe 가 first-class. 모든 layout · tiling 이 CuTe 로 통일. 강의 시점 (2024 April) 에는 3.x 가 main.

L0 → L1 → L2 로 갈수록 — 더 많은 코드를 작성해야 하고, 더 많은 자유를 얻고, 더 많이 잘못 짤 수 있다. 강의는 L1 ~ L2 의 boundary 에 머문다 — 사용자가 알아야 할 만큼만.

§ 03CUTLASS 코드의 telltale sign· _ 와 namespace

“이 코드가 CUTLASS 다” 를 1초 안에 알아보는 표식

Eric 의 강의 초반 — 코드를 펼쳤을 때 “이게 CUTLASS 인가” 를 즉시 알아보는 단서들. 처음 보는 사람에게 가장 도움이 되는 자리.

_ (underscore) placeholder
tile(_, _, k) — 의미는 “이 axis 는 그대로 둔다”. CuTe 의 indexing 규약. Python 의 : 와 비슷한 자리.
cute:: namespace
cute::Layout, cute::Shape, cute::Stride, cute::tile. 이 namespace 가 보이면 거의 CUTLASS 3.x.
_ Layout 안의 _
Layout<Shape<_8, _16>, Stride<_16, _1>>. compile time integer — 8, 16 같은 숫자가 type 안에 들어 있다. _8Int<8>.
tiled_mma / tiled_copy
CuTe 의 “tile + MMA” 또는 “tile + copy” 의 합성 객체. tensor core 가 어떻게 thread 사이에 뿌려지는지 표현.
SM75, SM80, SM90
arch tag — Turing, Ampere, Hopper. CUTLASS template 가 architecture 별로 instantiated.
Gemm template <A, B, C, D>
대부분의 CUTLASS GEMM 은 4 개 type tensor (A, B, C input, D output) 의 template. epilogue 가 D = α·(A@B) + β·C 형태.
실용 reading 순서

Eric 의 권고 — 새 CUTLASS 코드를 만나면 (1) 가장 위 using ... alias 들을 본다 (Layout, Shape 정의 — kernel 이 무엇을 어떤 모양으로 처리하는지가 여기 있음), (2) __global__ kernel 본문은 마지막에. 디테일이 alias 안에 다 들어 있다.

§ 04layout 의 본질· shape × stride

“tensor 가 메모리 어디 있는가” 의 가장 일반적인 표현

CuTe 의 핵심 — Layout = Shape × Stride. PyTorch 의 stride 와 비슷하지만 더 일반화. nested 가능, compile-time 가능, tile 합성에 닫혀 있다.

가장 단순한 layout — (8, 16) shape, (16, 1) stride 의 row-major 8×16 행렬. coordinate (i, j) 가 메모리 offset i·16 + j·1 = i·16 + j.

nested layout — ((4, 2), 16) shape 는 외부 4, 내부 2 의 두 단계 모드 + 마지막 모드 16. 같은 64 element 를 “4 그룹 × 2 element + 16” 으로 표현. 이게 hierarchical tiling 의 표현이 된다.

complex layout — column-major, transposed, swizzled (bank conflict 회피용 stride pattern) 모두 같은 Layout 추상으로 표현 가능.

// CUTLASS 3.x — CuTe Layout 의 예
using ALayout =
    cute::Layout<
      cute::Shape<_8, _16>,    // 8 × 16 tile
      cute::Stride<_16, _1>   // row-major
    >;

// nested — outer 4 그룹 × inner 8
using NestedLayout =
    cute::Layout<
      cute::Shape<cute::Shape<_4, _8>, _16>,
      cute::Stride<cute::Stride<_128, _16>, _1>
    >;

// 같은 데이터를 다양한 layout 으로 view
auto tA = cute::make_tensor(ptr, ALayout{});
왜 type 안에 숫자가 들어가는가

CUDA C++ template 으로 컴파일될 때 — _8 같은 compile-time 상수가 모든 multiplication 을 컴파일러가 풀게 한다. 결과: kernel 안의 indexing 이 instruction 1-2 개로 끝남. runtime 곱셈이 안 일어남. 이게 CUTLASS 의 zero-overhead abstraction 의 토대.

“layout 은 단순히 data 가 어디 있는가의 표현이 아니다 — 같은 data 를 어떻게 보느냐 의 함수다. 같은 메모리에 여러 layout 을 동시에 부여 가능.”학습 노트 paraphrase
§ 05tile 의 합성· divide / compose

큰 행렬 → CTA tile → warp tile → MMA tile — 같은 추상이 4 단계

CUTLASS GEMM 의 사고 핵심 — 같은 GEMM 을 여러 단계의 tile 로 동시에 분해한다. 그리고 그 분해 자체가 layout 의 합성 (compose) 으로 표현된다.

L0 · whole 전체 GEMMM × N × K (예: 4096 × 4096 × 4096) grid 결정의 단위
L1 · CTA tile 한 thread block 이 다루는 부분BlockM × BlockN × BlockK (예: 128 × 256 × 32) SRAM 으로 stage
L2 · warp tile 한 warp 이 누산하는 부분WarpM × WarpN (예: 64 × 64) register 위 누산기
L3 · MMA tile 한 instruction 이 다루는 부분16 × 16 × 16 (Ampere fp16) 또는 64 × 64 × 16 (Hopper WGMMA) tensor core 한 번
divide 의 의미

CuTe 의 tile(...) 또는 logical_divide(...) 가 — “이 큰 layout 을 작은 layout 단위로 자른다”. 결과는 nested layout. 외부 모드는 “몇 번째 tile” index, 내부 모드는 “tile 안의 element”. 같은 data 의 두 단계 view.

FIG · CTA tile 안의 layoutthread → element 매핑
T0
T0
T0
T0
T1
T1
T1
T1
T0
T0
T0
T0
T1
T1
T1
T1
T2
T2
T2
T2
T3
T3
T3
T3
T2
T2
T2
T2
T3
T3
T3
T3
한 8×8 sub-tile 이 4 thread (T0~T3) 에 어떻게 분할되는지. 각 thread 가 자기 4×4 fragment 를 register 에 들고 MMA. 이 매핑 자체가 또 layout — “thread index → element index” 의 layout.
§ 06tensor core 와 layout 의 만남· MMA 16×16×16

MMA instruction 이 요구하는 layout 을 어떻게 갖춰주는가

Tensor Core 의 MMA instruction 은 “이 thread 들이 input 의 어느 자리를 register 에 가지고 있어야 한다” 의 정확한 규약을 가진다. 사용자의 input layout 이 그 규약과 안 맞으면 — 추가 swizzling 또는 memory load 패턴이 필요. CUTLASS 는 이를 layout 합성으로 자동 처리.

예 — Ampere 의 m16n16k16.f16.f16.f32 MMA. 32 thread 가 한 warp 으로 일하는데:

  • A (16×16): 8 element per thread, register 8 개에 분포.
  • B (16×16): 8 element per thread, register 8 개.
  • C (16×16, accumulator): fp32 4 element per thread.
  • 각 thread 의 fragment 가 정확히 어디에 박혀야 하는지가 PTX 문서에 있음.

CUTLASS 는 이 매핑을 MMA_Atom 으로 추상화. 사용자는 using MMA = SM80_16x8x16_F32F16F16F32_TN; 같은 식으로 한 줄. 그 다음 tiled_mma 를 만들어 warp 안 thread 들에 자동 distribute.

// Ampere fp16 MMA atom
using MmaAtom = cute::MMA_Atom<
    cute::SM80_16x8x16_F32F16F16F32_TN
>;

// warp 마다 어떻게 tile 을 자르는지
using TiledMma = cute::TiledMMA<
    MmaAtom,
    cute::Layout<cute::Shape<_2, _2>>  // 2x2 warps
>;

// kernel 안에서 호출
auto tCgC = ...;   // thread 의 C fragment view
auto tArA = ...;   // A fragment
auto tBrB = ...;   // B fragment
cute::gemm(tiled_mma, tArA, tBrB, tCgC);
왜 swizzle 이 필요한가

MMA 에 들어가는 input 이 shared memory 에서 register 로 올라올 때 — 자연스러운 row-major 로 stage 하면 같은 cycle 에 여러 thread 가 같은 SRAM bank 를 친다 (bank conflict). swizzling 은 stride 를 살짝 비틀어서 같은 cycle 의 access 가 다른 bank 로 가게. CUTLASS 의 swizzle 은 layout 의 한 종류로 표현.

§ 07epilogue fusion· 사용자 정의 후처리

GEMM 결과가 register 에 있는 동안 — 거기서 다른 op 를 끝낸다

CUTLASS 가 cuBLAS 와 차별화되는 가장 큰 자리 — epilogue 안에 사용자 정의 op 를 끼울 수 있다. 누산기가 register 에 있고, 아직 HBM 으로 안 나간 시점에서 — D = act(α·(A@B) + β·C + bias) 같은 chain 을 한 kernel 안에 fuse.

대표 epilogue 패턴.

  • linear + bias + ReLU — Conv 또는 Linear 의 표준 후처리. cuBLAS 에는 일부만 있음, CUTLASS 는 자유.
  • quantization — fp32 accumulator → int8 output. dequant scale 곱 + clamp + cast 가 한 자리에.
  • FlashAttention 의 softmax + scaling — score 누산기 위에 online softmax 와 V 곱이 epilogue 안에 들어감 (사실은 main loop 도 일부 변형되지만 mental model 상).
  • 2:4 sparsity dequant — sparse GEMM 의 결과 위에 metadata 기반 변환.
// epilogue functor 의 pattern
struct MyEpilogue {
    float alpha, beta, scale;

    template<class AccTensor, class CTensor>
    __device__ void
    operator()(AccTensor& acc, CTensor& bias) {
        // register 에서 처리 — HBM 안 거침
        cute::transform(acc, [&](float x){
            float y = alpha * x + beta * bias;
            return y > 0 ? y : 0;  // ReLU
        });
    }
};
FlashAttention 과의 관계

FA 의 “Q×K^T → softmax → ×V” chain 이 결국 — 한 GEMM (Q×K^T) 의 epilogue 에 softmax 와 다음 GEMM (×V) 을 같이 fuse 한 형태. CUTLASS 가 이 수준의 fusion 을 지원해야 FA 같은 알고리즘이 production 으로 가능. § 08 에서 본격.

“epilogue fusion 은 단순한 ‘후처리 op’ 가 아니다 — register 위에서 다음 알고리즘을 시작할 수 있는 자리다.”학습 노트 paraphrase
§ 08FlashAttention 이 CUTLASS 위에 있는 이유· FA-2/3 의 base

FA 의 production 구현은 거의 모두 CUTLASS — 왜 그런가

FA-2 의 official repo (Tri Dao) 와 FA-3 (NVIDIA + Colfax) 는 CUTLASS template 위에 작성되어 있다. 강의에서 Eric 이 “왜 그게 자연스러운가” 를 짚는다.

이유 셋.

  1. tensor core 의 layout 정확히 다뤄야 함 — FA 는 attention score 의 N×N tile 을 register 위에 누산하는데, 그 layout 이 MMA instruction 과 정확히 맞아야 함. CUTLASS 의 layout 추상이 이걸 표현 가능.
  2. register accounting 이 critical — FA 의 m, l, O accumulator 가 register 에 박혀야 함 (§ L012 § 05 참조). CUTLASS 가 fragment 의 register 사용을 정확히 제어.
  3. 하나의 kernel 안에 두 GEMM + softmax 를 fuse — Q×K^T 와 P×V 가 같은 kernel 안에서 register 위 누산기를 공유. Triton 으로도 가능하지만 CUTLASS 가 더 정밀한 layout 통제.

그래서 FA 의 algorithm 자체는 Triton 으로도 표현 가능하지만 — peak performance 의 마지막 5-10% 는 CUTLASS 위에서만 가능한 자리.

FA-3 와 Hopper

FA-3 가 Hopper 의 새 instruction (TMA, WGMMA) 을 활용하는데 — 이 instruction 들이 CUTLASS 3.x 의 CuTe 추상에 first-class 로 통합되어 있다. 같은 kernel 을 Triton 으로 짜려면 manual lowering 이 필요한데 CUTLASS 에서는 builtin.

학습 메모

FA 의 flash_attention.cu (Thomas 의 L012 강의) 는 educational 코드 — 단순화. 실제 production FA repo 는 같은 알고리즘이지만 CUTLASS template 의 5000+ 줄.

§ 09언제 CUTLASS 로 내려가는가· 의사결정

cuBLAS / Triton 으로 안 풀리는 자리만 — 비용이 비싸다

Eric 이 강의에서 자기 직업 경험으로 정리한 의사결정 — CUTLASS 는 강력하지만 학습 곡선이 가파르다. 비용이 정당화되는 자리만 가야 한다.

CUTLASS 로 내려가는 정당한 신호

  • Triton 의 자동 layout 으로 못 잡는 bank conflict / register layout 문제 — NCU 의 instructions stalls 이 layout 관련.
  • cuBLAS + 별도 kernel 로 짠 fusion 이 한 kernel 안에 합치면 1.3-2× 빨라질 잠재성이 보일 때.
  • 새 hardware 의 새 instruction (TMA, WGMMA) 을 활용해야 할 때 — Triton 도 점차 따라잡지만 CUTLASS 가 항상 먼저.
  • vendor library 의 90% 가 아니라 95% 가 필요한 production critical path.

안 가도 되는 자리

  • memory-bound op (elementwise, normalize). CUTLASS 의 GEMM-grade 추상이 무의미.
  • 일반적인 LLM training/inference 의 거의 모든 layer — Triton 또는 cuBLAS 으로 충분.
  • fast prototyping. CUTLASS 는 컴파일 시간이 길고 디버깅이 어렵다.

Eric 의 직업적 입장 — “회사에서 CUTLASS 를 직접 짜는 사람은 보통 ML team 안에 1-2 명. 그 사람이 다른 ML 엔지니어와 협업해서 critical path 만 짠다.”

§ 10기억할 메모와 코드· key takeaways · repo
3-tier abstraction
cuBLAS (host) → CUTLASS (device kernel) → CuTe (layout primitive). 자기 task 가 필요한 깊이만.
Layout = Shape × Stride
CuTe 의 본질. compile-time integer 와 함께. 모든 indexing 이 zero-overhead 로 풀림.
_ placeholder
tile(_, _, k)_ 는 “이 axis 그대로”. CUTLASS 코드의 1초 식별자.
hierarchical tiling
whole → CTA → warp → MMA. 4 단계가 모두 layout 합성으로 표현. divide / compose.
MMA atom + tiled_mma
SM80_16x8x16_F32F16F16F32_TN 같은 atom. TiledMMA 가 atom 을 warp 들에 분배.
epilogue fusion
register 의 누산기 위에서 사용자 op 직접 작성. cuBLAS 의 black box 와 차이 나는 자리. FA, sparse, quant 의 base.
swizzle = layout
bank conflict 회피의 stride 패턴이 그저 또 다른 layout. 사용자가 직접 정의 가능.
FA 의 base
FA-2/3 의 production 구현이 모두 CUTLASS 위에. layout 통제 + register accounting + 두 GEMM fuse.
Slides repo 에 별도 슬라이드 없음 — 강의는 코드 + 화이트보드
Code github.com/ericauld · CUTLASS 원본: NVIDIA/cutlass
Repo gpu-mode/lectures 에 별도 lecture_015 폴더 없음

손에 새기기 — 실습 시퀀스

  1. CUTLASS examples 빌드NVIDIA/cutlass repo clone, examples/00_basic_gemm 컴파일. 첫 실행과 cuBLAS 비교.
  2. CuTe layout playground — examples/cute/tutorial 의 layout 예제 그대로 따라가며 print. shape/stride 의 nested 표현 손에 익히기.
  3. tile divide 직접 시도 — 64×64 행렬을 logical_divide 로 8×8 sub-tile 로 자르고, nested layout 의 두 모드 직접 indexing.
  4. Ampere MMA atom 한 번 — 16×16×16 fp16 MMA 를 한 warp 위에서 직접. fragment view 의 thread → element 매핑 print.
  5. epilogue functor 작성D = max(0, α·(A@B) + bias) 의 fused linear + ReLU + bias kernel. cuBLAS + 별도 kernel 대비 시간 측정.
  6. NCU 비교 — 같은 GEMM 을 cuBLAS, CUTLASS default, CUTLASS + custom epilogue 세 형태로. DRAM bytes 와 throughput 비교.
  7. FA repo 구조 읽기 — Tri Dao 의 flash-attention repo 의 csrc/flash_attn 디렉토리. CUTLASS template 의 어떤 자리가 어떻게 쓰이는지 — 코드 5 페이지 읽기.
§ 12열린 질문· open questions
  • CUTLASS 3.x → 4.x — 강의 시점 (2024 April) 이후의 major 변화. Hopper / Blackwell 지원의 진화. NVIDIA/cutlass repo 직접 추적 필요.
  • CuTe 와 Triton 의 비교 — 두 추상이 같은 자리를 다른 방식으로 푼다. 어느 쪽이 어떤 use case 에 우월한가의 systematic 비교가 강의에는 부족.
  • Hopper 의 WGMMA + TMA 의 정확한 활용 — 강의에서 짧게 언급. 코드 패턴 직접 보기.
  • FA-3 의 CUTLASS 사용 디테일 — § 08 에서 “CUTLASS 위에” 라고 적었지만, FA-3 가 어떤 part 에서 CUTLASS 의 어떤 부분을 쓰는지 정확한 매핑 필요.
  • swizzle 의 정확한 모양 — § 06 의 swizzle 이 layout 으로 표현된다고 적었지만, 실제 CuTe 의 Swizzle 타입 정확한 작동 — 별도 학습 필요.
  • compilation cost — CUTLASS template 의 컴파일 시간이 길다. 빌드 시스템에서 어떻게 풀어가는지 (precompile, kernel selection 등).
  • FlashInfer · ThunderKittens 같은 alternatives — CUTLASS 외 fused kernel 라이브러리의 비교. 강의 시점 이후 새 자리들.
검증 메모

이 노트의 namespace 와 type 이름 (SM80_16x8x16_F32F16F16F32_TN 등) 은 CUTLASS 3.x 의 실제 표기를 paraphrase 한 것이지만 — version 별로 정확한 형태가 약간 다르다. CUTLASS repo 의 include/cute/atom/mma_atom.hpp 에서 직접 확인.

← Lecture 014 Practitioners Guide to Triton — 한 단계 위의 추상에서 더 깊이로 Lecture 016 → On Hands Profiling — Taylor Robbie 가 깐 nsys/NCU 위 production 모델 진단