Coverage Report

Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/trueno/src/tiling/config.rs
Line
Count
Source
1
//! Tiling configuration and backend selection.
2
3
use serde::{Deserialize, Serialize};
4
use super::geometry::TcbGeometry;
5
use super::error::TilingError;
6
7
/// Complete tiling configuration for a kernel
8
///
9
/// Contains geometry for all three tiling levels, enabling hierarchical
10
/// cache-aware execution.
11
#[derive(Debug, Clone, Serialize, Deserialize)]
12
pub struct TilingConfig {
13
    /// Kernel name for identification
14
    pub name: String,
15
    /// Macro-tile geometry (L3/Global)
16
    pub macro_tile: TcbGeometry,
17
    /// Midi-tile geometry (L2/Shared)
18
    pub midi_tile: TcbGeometry,
19
    /// Micro-tile geometry (Registers)
20
    pub micro_tile: TcbGeometry,
21
    /// Target backend
22
    pub backend: TilingBackend,
23
}
24
25
/// Backend target for tiling configuration
26
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27
pub enum TilingBackend {
28
    /// CPU with AVX2 (256-bit SIMD)
29
    CpuAvx2,
30
    /// CPU with AVX-512 (512-bit SIMD)
31
    CpuAvx512,
32
    /// CPU with NEON (128-bit SIMD)
33
    CpuNeon,
34
    /// GPU (CUDA/wgpu)
35
    Gpu,
36
    /// Scalar fallback
37
    Scalar,
38
}
39
40
impl TilingConfig {
41
    /// Create configuration for GPU Q4_K MatVec
42
    ///
43
    /// Optimized for single-token generation where M=1.
44
    #[must_use]
45
0
    pub fn gpu_q4k_matvec() -> Self {
46
0
        Self {
47
0
            name: "Q4K_MatVec_GPU".into(),
48
0
            macro_tile: TcbGeometry::with_alignment(1, 4096, 256, 64),
49
0
            midi_tile: TcbGeometry::with_alignment(1, 256, 256, 64),
50
0
            micro_tile: TcbGeometry::with_alignment(1, 32, 256, 64),
51
0
            backend: TilingBackend::Gpu,
52
0
        }
53
0
    }
54
55
    /// Create configuration for GPU Q4_K MatMul (batched)
56
    ///
57
    /// Optimized for prefill where M > 1.
58
    #[must_use]
59
0
    pub fn gpu_q4k_matmul() -> Self {
60
0
        Self {
61
0
            name: "Q4K_MatMul_GPU".into(),
62
0
            macro_tile: TcbGeometry::with_alignment(128, 128, 256, 64),
63
0
            midi_tile: TcbGeometry::with_alignment(32, 32, 256, 64),
64
0
            micro_tile: TcbGeometry::with_alignment(8, 8, 256, 64),
65
0
            backend: TilingBackend::Gpu,
66
0
        }
67
0
    }
68
69
    /// Create configuration for GPU Softmax
70
    #[must_use]
71
0
    pub fn gpu_softmax() -> Self {
72
0
        Self {
73
0
            name: "Softmax_GPU".into(),
74
0
            macro_tile: TcbGeometry::with_alignment(1, 32000, 1, 64),
75
0
            midi_tile: TcbGeometry::with_alignment(1, 1024, 1, 64),
76
0
            micro_tile: TcbGeometry::with_alignment(1, 32, 1, 64),
77
0
            backend: TilingBackend::Gpu,
78
0
        }
79
0
    }
80
81
    /// Create configuration for CPU AVX-512 MatMul
82
    ///
83
    /// Optimized for 512-bit wide SIMD:
84
    /// - 16 floats per ZMM register
85
    /// - 32 ZMM registers available
86
    /// - 4×16 micro-kernel uses 8 registers (4 accumulators + 4 scratch)
87
    #[must_use]
88
0
    pub fn cpu_avx512_matmul() -> Self {
89
0
        Self {
90
0
            name: "MatMul_AVX512".into(),
91
0
            macro_tile: TcbGeometry::with_alignment(512, 512, 512, 64),
92
0
            midi_tile: TcbGeometry::with_alignment(128, 128, 128, 64),
93
0
            // 16 floats wide × 4 rows = 64 elements in registers
94
0
            micro_tile: TcbGeometry::with_alignment(4, 16, 128, 64),
95
0
            backend: TilingBackend::CpuAvx512,
96
0
        }
97
0
    }
98
99
    /// Create configuration for CPU AVX-512 Q4K MatVec
100
    ///
101
    /// Optimized for Q4_K quantized inference with 512-bit SIMD.
102
    /// Key differences from AVX2:
103
    /// - 64-byte aligned for cache line optimization
104
    /// - 4×1 micro-kernel processes 4 rows simultaneously
105
    /// - K=256 aligned to Q4_K superblock
106
    #[must_use]
107
0
    pub fn cpu_avx512_q4k_matvec() -> Self {
108
0
        Self {
109
0
            name: "Q4K_MatVec_AVX512".into(),
110
0
            // Large macro-tile to amortize L3 access
111
0
            macro_tile: TcbGeometry::with_alignment(4096, 1, 4096, 64),
112
0
            // Midi-tile fits in L2 (256KB)
113
0
            // 64 rows × 256 K × 0.5625 bytes/element ≈ 9KB weights
114
0
            midi_tile: TcbGeometry::with_alignment(64, 1, 256, 64),
115
0
            // 4 rows × 1 output, K=256 (Q4_K superblock)
116
0
            micro_tile: TcbGeometry::with_alignment(4, 1, 256, 64),
117
0
            backend: TilingBackend::CpuAvx512,
118
0
        }
119
0
    }
120
121
    /// Create configuration for AVX-512 VNNI Q4K×Q8K integer dot product
122
    ///
123
    /// AVX-512 VNNI (Vector Neural Network Instructions) provides:
124
    /// - VPDPBUSD: 8-bit unsigned × 8-bit signed multiply-add to i32
125
    /// - VPDPWSSD: 16-bit signed × 16-bit signed multiply-add to i32
126
    ///
127
    /// This enables pure integer Q4K×Q8K without intermediate f32 conversion.
128
    #[must_use]
129
0
    pub fn cpu_avx512_vnni_q4k_q8k() -> Self {
130
0
        Self {
131
0
            name: "Q4K_Q8K_VNNI".into(),
132
0
            macro_tile: TcbGeometry::with_alignment(4096, 1, 4096, 64),
133
0
            midi_tile: TcbGeometry::with_alignment(64, 1, 256, 64),
134
0
            // VNNI processes 64 i8 values per ZMM register
135
0
            micro_tile: TcbGeometry::with_alignment(4, 1, 256, 64),
136
0
            backend: TilingBackend::CpuAvx512,
137
0
        }
138
0
    }
139
140
    /// Create configuration for CPU AVX2 MatMul
141
    #[must_use]
142
0
    pub fn cpu_avx2_matmul() -> Self {
143
0
        Self {
144
0
            name: "MatMul_AVX2".into(),
145
0
            macro_tile: TcbGeometry::with_alignment(256, 256, 256, 32),
146
0
            midi_tile: TcbGeometry::with_alignment(64, 64, 64, 32),
147
0
            // 8 floats wide × 4 rows = 32 elements in registers
148
0
            micro_tile: TcbGeometry::with_alignment(4, 8, 64, 32),
149
0
            backend: TilingBackend::CpuAvx2,
150
0
        }
151
0
    }
152
153
    /// Create configuration for CPU Q4_K MatVec (AVX2)
154
    #[must_use]
155
0
    pub fn cpu_avx2_q4k_matvec() -> Self {
156
0
        Self {
157
0
            name: "Q4K_MatVec_AVX2".into(),
158
0
            // Process 4 rows at a time (4×1 micro-kernel)
159
0
            macro_tile: TcbGeometry::with_alignment(4096, 1, 4096, 32),
160
0
            midi_tile: TcbGeometry::with_alignment(64, 1, 256, 32),
161
0
            // 4 rows × 1 output, K=256 (Q4_K superblock)
162
0
            micro_tile: TcbGeometry::with_alignment(4, 1, 256, 32),
163
0
            backend: TilingBackend::CpuAvx2,
164
0
        }
165
0
    }
166
167
    /// Create configuration for RMSNorm (CPU)
168
    #[must_use]
169
0
    pub fn cpu_rmsnorm() -> Self {
170
0
        Self {
171
0
            name: "RMSNorm_CPU".into(),
172
0
            macro_tile: TcbGeometry::with_alignment(1, 4096, 1, 32),
173
0
            midi_tile: TcbGeometry::with_alignment(1, 256, 1, 32),
174
0
            micro_tile: TcbGeometry::with_alignment(1, 16, 1, 32),
175
0
            backend: TilingBackend::CpuAvx512,
176
0
        }
177
0
    }
178
179
    /// Validate that tiling configuration is internally consistent
180
0
    pub fn validate(&self) -> Result<(), TilingError> {
181
        // Macro must be >= Midi >= Micro
182
0
        if self.midi_tile.m > self.macro_tile.m
183
0
            || self.midi_tile.n > self.macro_tile.n
184
0
            || self.midi_tile.k > self.macro_tile.k
185
        {
186
0
            return Err(TilingError::InvalidHierarchy {
187
0
                reason: "Midi-tile larger than macro-tile".into(),
188
0
            });
189
0
        }
190
191
0
        if self.micro_tile.m > self.midi_tile.m
192
0
            || self.micro_tile.n > self.midi_tile.n
193
0
            || self.micro_tile.k > self.midi_tile.k
194
        {
195
0
            return Err(TilingError::InvalidHierarchy {
196
0
                reason: "Micro-tile larger than midi-tile".into(),
197
0
            });
198
0
        }
199
200
        // Check divisibility
201
0
        if self.macro_tile.m % self.midi_tile.m != 0 {
202
0
            return Err(TilingError::DivisibilityError {
203
0
                level: "macro/midi",
204
0
                dimension: "M",
205
0
                larger: self.macro_tile.m,
206
0
                smaller: self.midi_tile.m,
207
0
            });
208
0
        }
209
210
0
        if self.midi_tile.m % self.micro_tile.m != 0 {
211
0
            return Err(TilingError::DivisibilityError {
212
0
                level: "midi/micro",
213
0
                dimension: "M",
214
0
                larger: self.midi_tile.m,
215
0
                smaller: self.micro_tile.m,
216
0
            });
217
0
        }
218
219
0
        Ok(())
220
0
    }
221
222
    /// Calculate total number of macro-tiles for given problem size
223
    #[must_use]
224
0
    pub fn num_macro_tiles(&self, m: u32, n: u32) -> u32 {
225
0
        let m_tiles = (m + self.macro_tile.m - 1) / self.macro_tile.m;
226
0
        let n_tiles = (n + self.macro_tile.n - 1) / self.macro_tile.n;
227
0
        m_tiles * n_tiles
228
0
    }
229
230
    /// Calculate total number of midi-tiles within a macro-tile
231
    #[must_use]
232
0
    pub fn midi_tiles_per_macro(&self) -> u32 {
233
0
        let m_tiles = self.macro_tile.m / self.midi_tile.m;
234
0
        let n_tiles = self.macro_tile.n / self.midi_tile.n;
235
0
        m_tiles * n_tiles
236
0
    }
237
238
    /// Calculate total number of micro-tiles within a midi-tile
239
    #[must_use]
240
0
    pub fn micro_tiles_per_midi(&self) -> u32 {
241
0
        let m_tiles = self.midi_tile.m / self.micro_tile.m;
242
0
        let n_tiles = self.midi_tile.n / self.micro_tile.n;
243
0
        m_tiles * n_tiles
244
0
    }
245
}