@@ -81,4 +81,83 @@ constexpr int kRMSNormBlockSize = 256;
8181// Attention constants
8282constexpr int kAttentionBlockSize = 256 ;
8383
84+ // ---- Architecture tier detection and per-arch kernel tuning ----
85+ //
86+ // RocmArchTier provides fine-grained GPU generation identification.
87+ // ArchTuning holds per-arch parameters for kernel dispatch decisions.
88+ // Both are usable from host code and kernel dispatch logic.
89+
90+ enum class RocmArchTier {
91+ Rdna2, // gfx10xx: RDNA 2, Wave32, no WMMA
92+ Rdna3, // gfx1100-gfx1103: RDNA 3, Wave32, WMMA, 96KB LDS
93+ Rdna35, // gfx1150-gfx1152: RDNA 3.5, Wave32, WMMA, 64KB LDS, 32MB IC
94+ Rdna4, // gfx1200-gfx1201: RDNA 4, Wave32, enhanced WMMA
95+ Cdna, // gfx9xx: MI-series, Wave64
96+ };
97+
98+ // Hardware capabilities detected at runtime from hipDeviceProp_t.
99+ struct HWInfo {
100+ RocmArchTier tier;
101+ int num_cus; // Compute units (multiProcessorCount)
102+ int simds_per_cu; // SIMDs per CU (2 for RDNA, 4 for CDNA)
103+ int max_threads_per_cu; // Max resident threads per CU
104+ int shared_mem_per_cu; // Shared/LDS memory per CU in bytes
105+ int l2_cache_bytes; // L2/Infinity Cache size
106+ bool has_wmma; // WMMA/tensor core support
107+ };
108+
109+ // Per-architecture tuning parameters for quantized matvec and attention kernels.
110+ struct ArchTuning {
111+ // QMV tiled kernel
112+ int qmv_tile_n; // Output columns per block (L2 reuse)
113+ // QMV↔GEMM crossover M thresholds
114+ int qmv_crossover_small; // For K<=2048, N<=2048
115+ int qmv_crossover_medium; // For K<=4096, N<=4096
116+ int qmv_crossover_large; // For larger shapes
117+ // Flash attention
118+ int fa_block_m; // Queries per flash attention block
119+ int fa_block_n; // Keys per iteration
120+ };
121+
122+ // Auto-tune based on detected hardware. Adjusts tile sizes based on actual
123+ // CU count to balance occupancy vs L2 reuse.
124+ inline ArchTuning get_arch_tuning (RocmArchTier tier) {
125+ // Defaults per tier — used when HWInfo isn't available
126+ switch (tier) {
127+ case RocmArchTier::Rdna2:
128+ return ArchTuning{8 , 28 , 20 , 14 , 128 , 64 };
129+ case RocmArchTier::Rdna3:
130+ return ArchTuning{16 , 36 , 24 , 16 , 64 , 64 };
131+ case RocmArchTier::Rdna35:
132+ // 40 CUs: TILE_N=16 gives best occupancy/reuse balance
133+ return ArchTuning{16 , 36 , 24 , 16 , 64 , 64 };
134+ case RocmArchTier::Rdna4:
135+ return ArchTuning{32 , 40 , 28 , 18 , 64 , 64 };
136+ case RocmArchTier::Cdna:
137+ default :
138+ return ArchTuning{16 , 20 , 14 , 10 , 128 , 64 };
139+ }
140+ }
141+
142+ // Auto-tune using full hardware info. Adjusts TILE_N based on CU count:
143+ // fewer CUs → larger tiles for more L2 reuse per block.
144+ inline ArchTuning get_arch_tuning (const HWInfo& hw) {
145+ auto t = get_arch_tuning (hw.tier );
146+
147+ // Auto-tune QMV tile_n based on CU count.
148+ // Benchmarking shows TILE_N=16 is optimal for RDNA 3/3.5 regardless
149+ // of CU count — TILE_N=32 creates 1024-thread blocks that reduce
150+ // occupancy. Only go to 8 for very low CU counts.
151+ if (hw.tier == RocmArchTier::Rdna3 || hw.tier == RocmArchTier::Rdna35 ||
152+ hw.tier == RocmArchTier::Rdna4) {
153+ if (hw.num_cus <= 16 ) {
154+ t.qmv_tile_n = 8 ; // Very small APU: maximize occupancy
155+ } else {
156+ t.qmv_tile_n = 16 ; // All other RDNA 3+: best balance
157+ }
158+ }
159+
160+ return t;
161+ }
162+
84163} // namespace mlx::core::rocm
0 commit comments