1真正的二維 tile blocking最大關鍵
切成 MT×NT 的 tile(64/128 × 64/128),grid =(⌈n/NT⌉, ⌈m/MT⌉),M 走 grid.y。每個載入的 B 元素被 MT 列重用、每個 A 元素被 NT 欄重用 → 每 byte 的算術強度從 O(1) 拉到 MT·NT/(MT+NT)。早期版本 grid 只有一維(只切 N)、每個 block 掃完整個 M,等於沒有 blocking,Matrix Core 一直在等運算元。
2B 走 LDS 共用、A 常駐 VGPR
B 由整個 workgroup 協同從 global 搬進 LDS,所有 wavefront 再從 LDS 讀 MFMA operand 佈局 → B 的 global 流量除以 wave 數。A 則烤成 MFMA lane-major([row16][kg_pair][lane][which]),lane 用一次 128-bit f64x2 載入就拿到連續兩個 K group,而且全程只待在 VGPR,LDS 100% 留給 B。早期版本 B 在 MFMA 迴圈裡直接讀 global,每個 wavefront 各讀一次同樣的 B。
3雙緩衝 PGR2 三段流水
A 預取到兩組 VGPR;B 是三段流水:算 curbuf 的 MFMA ‖ 把 k_next 寫進 LDS ‖ 把 k_next2 從 global 預取進暫存器。global 延遲完全被算術蓋掉。早期版本零預取,每次 MFMA 都在等載入回來。
4workgroup swizzle + fast/slow path 分家
block id 改成 column-major 走訪(m_tile = bid % gridDim.y; n_tile = bid / gridDim.y),讓同時在跑的 workgroup 共用同一個 N tile,B tile 直接命中 L2 / Infinity Cache。另外整個 tile 都在界內時走一份完全沒有邊界判斷的內層迴圈與 epilogue,越界的 M tile 在生成期就從程式碼裡消失。早期版本平鋪一維 index、全程帶 guard。
5候選交給 autotuner + 向量化 epilogue
一次吐出 4 組 (MT, NT) × width{1,2} 共 8 個候選讓 PyFR 挑最快;aligne 為偶數時 NT 轉成 double2 欄,B 載入 / C 寫出都是 16 B;β=1 時所有 C 的 nt_load 先集中發完再集中 nt_store,讓載入互相重疊。並且只在 gfx942 / gfx950 生成候選,避免在未驗證架構上浪費 autotune 時間。早期版本只有一組寫死的 blockx=64、width 恆為 1、epilogue 逐點序列化。