今天來複習從 Day11 到 Day19,我們從 GPU 硬體出發,認識 CUDA 與幾種 kernel 開發介面,再沿著一個 Triton 矩陣乘法一路打開 TTIR、TTGIR、LLVM IR、PTX、cubin 與 SASS。最後,CUDA Driver 載入 binary 並把 kernel 送進 GPU。
今天要把這九天的內容接起來,從一串 artifact 整理出三個之後也能帶去其他 compiler 與 accelerator 的問題
| 位置 | 要回答的問題 | Triton NVIDIA 案例 |
|---|---|---|
| Hardware | 工作由哪些執行單元處理?資料放在哪裡? | SM、warp、register、shared memory、L2、global memory、Tensor Core |
| Programming abstraction | 開發者描述 thread、tile、layout,還是直接呼叫 operation? | @triton.jit 與 block of values |
| Compiler representations | 哪些資訊仍被保留?哪些決策已經具體化? | TTIR → TTGIR → LLVM IR → PTX |
| Target binary | 哪些內容已綁定目標 GPU? | cubin、SASS、resource metadata |
| Runtime | Binary 如何成為一次實際執行? | CUmodule、CUfunction、launch configuration、stream |
這五個位置不要求每套系統都使用相同名稱。例如 cuTile 的公開 IR 邊界是 CUDA Tile IR,Triton NVIDIA backend 可看到 TTIR、TTGIR、LLVM IR 與 PTX,換到 AMD 或其他 accelerator,最後的 ISA、binary 格式與 runtime API 也會改變。讀法仍然相同,找出每一站的輸入、輸出,以及這一站新增的資訊。

Day11 把 GPU kernel 拆成三個互相連動的角度
這些硬體名詞會在後面的 compiler artifact 重新出現。TTGIR 的 layout 描述 element 如何分給 warp 與 lane,LLVM IR 的 address space 區分 global 與 shared memory;ptxas 的 register 配置影響同一個 SM 能同時容納多少工作,launch configuration 則帶入 grid、block 與 dynamic shared-memory 大小。

橫軸是開發者主要描述的單位,縱軸是開發者直接指定硬體細節的程度。CUDA C++ 可以明確安排 thread、memory space、同步與 inline PTX;CuTe DSL 則以 layout、copy、MMA 等結構化抽象控制硬體。兩者控制的面向不同,無法只靠一條由低到高的直線完整比較。
還有兩組容易混淆的 Python DSL
CuTe DSL 屬於 CUTLASS 的 Python DSL 家族。它保留 layout、tensor、copy atom、MMA atom 與 pipeline 等硬體導向抽象,適合需要直接設計資料搬移與 Tensor Core kernel 的工作。
官方文件把 code generation 拆成三個階段
Python AST preprocessing
→ Python interpreter 執行 meta-stage tracing
→ 產生 internal IR
→ backend lowering 與最佳化
→ PTX/SASS 與 device binary
→ runtime load / launch
Python 在 meta-stage 處理 metaprogramming、compile-time value 與控制流程,產生的 kernel 再交給 backend。這讓開發者避開大量 C++ template metaprogramming,同時保留對 layout、copy、MMA 與硬體執行階層的控制。
cuTile 是建立在 CUDA Tile programming model 上的 Python DSL。Kernel 在 logical grid of blocks 上執行;每個 block 操作 immutable tile values,編譯器再決定 block 內的 thread mapping、實體儲存位置與可使用的硬體能力。
Python @ct.kernel
→ 依實際參數 specialization
→ CUDA Tile IR/TileIR bytecode
→ tileiras + libNVVM + ptxas
→ cubin
→ CUDA launch
目前官方 quickstart 列出的支援範圍已包含 compute capability 8.x 到 12.x ,不再只適用最初推出時的 Blackwell。cuTile 也支援 AOT 匯出 TileIR bytecode 或指定 GPU 的 cubin。支援版本仍會隨 CUDA Toolkit 與 cuTile 套件更新,實際使用前應重新檢視 release notes。
Triton kernel 由 grid 上的 program instances 組成。一個 program instance 通常處理一塊資料,使用 tl.program_id、tl.arange、tl.load、tl.store 與 tl.dot 表達 block-level 運算。開發者負責 grid、tiling strategy 與 meta-parameters;compiler 負責更細的資料布局與 thread mapping。
cuTile 與 Triton 都讓使用者從 tile/block 出發,但 API、IR、backend、可觀察 artifact 與可調參數不同。CuTe DSL 提供另一條路:開發者仍直接操作更接近硬體的 layout、copy 與 MMA 抽象。

Day13 我們一起讀 MLIR paper,了解現代 compiler 為什麼需要多層 IR。高階表示適合保留 tensor、shape 與 operation,靠近硬體的表示需要 control flow、address space、register 與 target instruction。太早丟掉高階語意,compiler 很難再做 tile、fusion 或 layout transformation;停在高階表示,也無法產生可執行程式。
MLIR 提供 dialect、operation、type、attribute、pass、rewrite 與 verification 等共用機制。它允許一個 compiler 定義多個適合不同問題的表示,再用 lowering 逐步改寫。
然後設計一層 IR 需要去思考,這一層要保留哪些資訊,以及哪些 transformation 需要這些資訊。

@triton.jit 會把 Python function 包成可交給 Triton compiler 的 JITFunction。執行 kernel[grid](...) 時,runtime 會綁定參數、建立 specialization key、查詢 cache,必要時才啟動 compiler。
可以先用這條通用流程理解
kernel source
+ runtime types/constexpr/compile options/target
→ specialization key
→ cache lookup
→ miss 時編譯
→ binary + metadata
→ load + launch
這也把 benchmark 分成幾種不同成本,第一次編譯、硬碟 cache 命中、process 內已載入 module,以及純 kernel execution。

Triton frontend 走訪 Python AST,建立初始 Triton IR。進入 TTIR passes 後,artifact 仍保留 tt.load、tt.store、tt.dot、scf.for、tensor shape 與 pointer 計算等語意。
TTIR 適合回答
program_id 是否選到正確的 output tile?constexpr 與 specialization 是否產生預期常數?如果錯誤已經出現在 shape、mask 或 pointer,繼續追 PTX 只會增加閱讀成本。先在最接近原始語意的 artifact 確認 correctness,再往硬體映射移動。

TTGIR 開始加入 target 與 GPU layout。相同的 tensor shape 會多出 element 如何分給 CTA、warp 與 lane 的 encoding,也能看到 global-memory coalescing、shared-memory staging、layout conversion、MMA representation 與 software pipeline。
以矩陣乘法簡化後的資料路徑為例
A / B global tiles
→ shared-memory staging
→ warp / lane register fragments
→ Tensor Core MMA
→ accumulator
→ C global memory
BLOCK_SIZE_M/N/K、num_warps、num_stages 與 target architecture 都可能改變映射。Compiler pipeline 中出現某個 pass,也不保證這支 kernel 一定使用對應硬體路徑;仍要回到實際 TTGIR、metadata 與 profiler 證據確認。

從 TTGIR 進入 LLVM IR 時,lowering 會處理 Triton GPU operations,輸出 LLVM NVPTX backend 能接收的表示
phi 等 control flow。例如 TTGIR 的 shared-memory descriptor 會變成具體的 addrspace(3) 資源;GPU thread id、barrier 與部分 matrix instructions 則會以 NVVM intrinsic 或 inline assembly 表示。到這一站,tl.load 這類 Triton 語意已大幅降低,但 GPU-specific information 仍然清楚可見。

LLVM NVPTX backend 接收 LLVM module,產生 PTX assembly text。PTX 會包含 kernel entry、address space、virtual registers 與 NVIDIA GPU operations。
接著,ptxas 針對指定的 sm_XX 目標產生 cubin
LLVM IR
→ LLVM NVPTX backend
→ PTX virtual ISA
→ ptxas
→ cubin:SASS machine code + resource metadata
ptxas 會完成 physical register allocation、target-specific instruction encoding 與 binary metadata。ptxas -v 提供的 register、spill、stack frame、constant memory 與 shared-memory 用量,可以接回 Day11 的 SM 資源限制。
PTX 的 %r163 是 virtual register,不能直接當成硬體實際使用 163 個 registers。應檢視 ptxas log、cubin metadata 或 profiler,才能判斷 physical register 壓力。

cubin 是 compiler 的輸出,仍需要 runtime 把它變成一次具體執行。在 Triton 3.6.0 NVIDIA launcher 中,可以簡化成
cubin bytes
→ cuModuleLoadData()
→ CUmodule
→ cuModuleGetFunction()
→ CUfunction
→ grid / block / dynamic shared memory / stream / arguments
→ cuLaunchKernelEx()
→ GPU 執行 SASS
Module load 與 function lookup 建立目前 CUDA context 可使用的 handle,launch configuration 則描述這一次要執行多少工作、使用哪個 stream、傳入哪些 arguments。
Kernel launch 對 host 通常是非同步 enqueue。API 成功回傳只代表工作已送入 queue,並不等於 GPU 已完成。讀回結果、定位非同步錯誤或量測執行時間時,需要在正確位置使用 CUDA event 或 synchronization。

過一次整體流程
Compile path
Python AST
→ TTIR
→ TTGIR
→ LLVM IR
→ LLVM NVPTX backend
→ PTX
→ ptxas
→ cubin / SASS
Runtime path
cubin
→ CUmodule
→ CUfunction
→ launch configuration
→ CUDA stream
→ GPU execution
編譯路徑到可載入的 target binary 為止。Runtime path 從載入 binary 開始,處理 context、function handle、arguments、stream 與 launch。把兩條路分開後,cache、module load、launch latency 與 kernel execution time 也比較不容易混淆。
| 症狀 | 優先檢查 | 原因 |
|---|---|---|
| Shape、mask、pointer 或數值語意不符預期 | TTIR | 最接近 Triton source,仍保留 block-level operation |
| Layout conversion、shared-memory staging、warp mapping、MMA 路徑異常 | TTGIR | GPU layout 與 pipeline 在這裡具體化 |
| Address space、control flow、NVVM intrinsic、libdevice 問題 | LLVM IR | 已進入 LLVM/NVPTX 可處理的低階表示 |
.entry、PTX version、target 或 virtual instruction 問題 |
PTX | 可直接看到 PTX header 與 virtual ISA |
| Register、spill、組譯失敗 | ptxas log |
Physical resource allocation 在這一站完成 |
| 想確認實際 machine instruction | cubin/SASS | 已綁定目標 SM 的 machine code |
| Module 載入或 symbol lookup 失敗 | CUDA module API | Binary 已產生,問題位於 context/module/function |
| Invalid launch configuration、stream 或非同步錯誤 | Launch API + synchronization | 問題與這次執行設定或非同步時序有關 |
| 結果正確但速度不如預期 | Artifact + metadata + profiler | 靜態程式、resource usage 與實際時間線要一起看 |
明天開始進入 Tenstorrent 好耶。硬體名稱會換成 Tensix core、L1 SRAM 與 NoC,軟體堆疊會看到 TT-Metalium 與 TT-MLIR。我們可以沿用今天的五個問題:程式從哪個抽象開始、有哪些 IR、tile 如何映射到 core、資料如何移動、runtime 如何載入並啟動工作。