GEMM in CUDA C++ and Triton, torch.compile and ROCm portability

Illustrative electricity transmission towers, not GPU hardware.
An analytical deep dive into those three frontier areas—GEMM implementation in CUDA C++ vs. Triton, torch.compile (TorchInductor) automated code generation, and AMD ROCm cross-vendor portability—reveals the practical engineering trade-offs behind modern AI systems.
1. Side-by-Side Analysis: Matrix Multiplication (GEMM) in CUDA C++ vs. Triton
Matrix multiplication (C = A × B) is the ultimate benchmark for GPU programming because it is compute-bound and must feed Tensor Cores continuously without stalling on memory.
- Modern CUDA C++ (CUTLASS / CuTe Approach):
- Manual Hardware Orchestration: In CUDA C++, achieving peak GEMM speed requires dividing work across a strict hierarchy: Grid → Thread Block (CTA) → Warpgroup (4 warps) → Warp → Tensor Core instruction (wgmma).
- Explicit Pipelining & Swizzling: Engineers must manually allocate double- or triple-buffered Shared Memory stages (cp.async or TMA) so that while Tensor Cores multiply Tile N, asynchronous hardware engines load Tile N+1. They must also apply XOR memory swizzling to prevent Shared Memory bank conflicts.
- Analytical Verdict: Unmatched peak performance (90%–98% of theoretical Tensor Core FLOPS), but requires hundreds of lines of brittle, architecture-specific C++ template code.
- OpenAI Triton Approach:
- Declarative Tile Math: In Triton, a high-performance tiled GEMM takes roughly 40 lines of Python. The engineer writes a loop over the inner dimension K, loading 2D blocks (tl.load) and calling tl.dot(a, b) to accumulate partial sums.
- Compiler-Driven Pipelining: Triton’s MLIR backend automatically infers software pipelining stages (num_stages=3), inserts Shared Memory swizzling, and emits Tensor Core instructions. Additionally, @triton.autotune tests different tile sizes (BLOCK_M, BLOCK_N, BLOCK_K) on the target GPU to pick the fastest configuration automatically.
- Analytical Verdict: Delivers 85%–95% of CUTLASS performance with 10× less code, making it superior for custom fused GEMMs (e.g., GEMM + Bias + GeLU + Quantization) where standard libraries fall short.
2. Analysis of torch.compile (TorchInductor): How PyTorch Writes Triton Automatically
PyTorch 2.0’s torch.compile bridges the gap between eager Python flexibility and compiled kernel speed by using TorchInductor to generate Triton code automatically.
[Eager Python Code]
│ 1. TorchDynamo (Bytecode Capture)
▼
[FX Graph (ATen Ops)]
│ 2. AOTAutograd (Forward + Backward Decomposition)
▼
[TorchInductor Loop IR]
│ 3. Operator Fusion & Memory Layout Analysis
▼
[Generated Triton Python Kernel] ──► [PTX / SASS Binary]- How Automated Fusion Works:
- When you decorate a model with @torch.compile, TorchDynamo intercepts Python bytecode and captures a static computation graph without Python interpreter overhead.
- TorchInductor lowers ~2,000 high-level PyTorch operators into ~250 core primitives, then analyzes memory dependencies to group adjacent pointwise (e.g., add, mul, silu) and reduction (e.g., sum, mean, softmax) operations together.
- Instead of calling separate pre-compiled C++ kernels, TorchInductor emits a brand-new Triton Python script on disk (viewable via TORCH_LOGS="output_code"), compiles it via Triton's MLIR backend, and caches the resulting GPU binary.
- Analytical Trade-Offs (Strengths vs. Limits):
- Strength: Eliminates manual kernel writing for 80%–90% of standard model bottlenecks, routinely accelerating training and inference by 1.5× to 2.5× with a single line of code.
- Limitation: Suffers from graph breaks when encountering unsupported dynamic Python control flow, and still struggles to automatically discover complex algorithmic reformulations (such as FlashAttention's online Softmax tiling), which still require hand-written Triton or CUDA kernels.
3. Analysis of Cross-Vendor Portability: NVIDIA CUDA vs. AMD ROCm
For years, NVIDIA’s proprietary CUDA ecosystem created a near-monopoly on AI compute. Modern compiler stacks have systematically eroded this moat by decoupling high-level AI code from vendor-specific hardware backends.
| Portability Layer | NVIDIA Ecosystem | AMD ROCm Ecosystem | Analytical Comparison |
|---|---|---|---|
| Low-Level C++ Kernels | CUDA C++ (nvcc → PTX → SASS) | HIP (hipcc → AMDGPU LLVM → GCN/CDNA) | High Friction. AMD’s hipify tool translates CUDA syntax to HIP, but warp sizes differ (32 threads on NVIDIA vs. 64 threads on AMD CDNA), breaking hardcoded warp-shuffle math. |
| Block-Level Kernels | OpenAI Triton (NVPTX Backend) | OpenAI Triton (AMDGPU Backend) | Near-Seamless. Because Triton abstracts away warp sizes behind 2D blocks, a single Triton kernel runs natively on both NVIDIA H100/B200 and AMD Instinct MI300X/MI325X. |
| Framework Level | PyTorch + torch.compile + vLLM | PyTorch + torch.compile + vLLM | Mature Parity. Major inference engines (vLLM, SGLang) and PyTorch run out-of-the-box on ROCm by routing custom ops through Triton and AMD’s rocBLAS/CK (Composable Kernel). |
- Strategic Takeaway: While hand-written CUDA C++ remains locked to NVIDIA silicon and requires manual porting to AMD HIP, OpenAI Triton acts as a portable hardware abstraction layer. AI teams that standardize custom kernels on Triton can deploy the exact same codebase across NVIDIA and AMD clusters without maintaining separate C++ codebases.