cudatraining · lab notes

LESSON 08 · 2026.04.19 · L4

Triton vs CUDA — where does the cost of abstraction show up?

Four kernels from lessons 1–6 (reduction, softmax, matmul, flash attention) rewritten in 40–130 lines of Triton. Four regimes measured on L4 (sm_89).

GPU · L4 (sm_89) stack · torch 2.6 + triton 3.2 ported · 4 kernels

Hardware upgrade — why L4

T4 has only one FP16 WMMA flavor and no TF32. To measure Triton tl.dot automatically picking TF32, you need a GPU with TF32 Tensor Cores. L4 (Ada) = TF32 121 · FP16 242 · FP8 485 TFLOPS, L2 48 MB (8× T4's).

Memory-bound — three approaches tie within 10%

taskCUDAtorchTriton
Reduction 67M fp32258 GB/s254 GB/s245 GB/s
Softmax 4096² fp32237 GB/s240 GB/s221 GB/s

Triton lands at 93–95% of hand-written CUDA. When HBM is the bottleneck, the abstraction tax evaporates.

Compute-bound — Triton narrowly beats cuBLAS

taskCUDAtorchTriton
matmul 4096³ FP32 (TF32)3.9 TF25.828.9
matmul 4096³ FP1618.5 TF51.854.0

2.9× over our WMMA. Over cuBLAS: FP32 +12%, FP16 +4%. The spot where autotune narrowly edges human hand-tuning.

Flash Attention — Triton fp16 is polyglot

NCUDA FA fp32Triton fp32Triton fp16SDPA fp16
10240.3240.1480.1220.076
20480.6380.1960.1380.076
40961.2560.3580.2070.127
81923.0451.1180.4960.394

N=8192: Triton fp16 is 6.14× over our CUDA FP32, and 79% of SDPA (cuDNN FA-2). 100 lines of Triton reaches 80% of cuDNN. This is why Tri Dao wrote FA-2 in Triton.

One line = dozens of lines, repeated four times

Regions where the abstraction cost is real

regionTriton vs CUDAcause
Small N (< 4 MB)3–12× slowerLaunch floor 50–100 µs (Python → autotune cache → JIT → cuLaunch)
HBM-bound95%almost none
Large matmul / FAwinsautotune picks a better config than a human

Practical line: if the Transformer layer is ≥1 ms, a 100 µs overhead is <10% — tolerable. If you launch 30 element-wise ops individually in Triton, you're cooked.

Two footguns

(1) TF32 benchmark lie. torch.matmul(fp32) doesn't use TF32 by default. tl.dot does. Compare them head-to-head and "Triton beats torch 2×" — which looks great but is wrong. For a fair fight:

torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True

(2) Autotune stale writes. @triton.autotune trials can leave tainted writes in the output buffer. Use reset_to_zero=["partial_ptr"] and slice with best_config.kwargs["BLOCK_SIZE"] after the call.

Why keep learning CUDA

  1. Triton hits a wall and you fall back to CUDA (Blackwell mma, persistent kernels, async copy).
  2. You need to read the PTX Triton emits to chase perf bugs.
  3. vLLM, FA-3, Mamba are still CUDA.
  4. The answer to "why is this slow" is bank conflict, register spill, occupancy — all CUDA concepts.

CUDA = assembly, Triton = C. Write most of it in C; reserve assembly for hot paths.