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/geometry.rs
Line
Count
Source
1
//! TCB geometry types and level definitions.
2
3
use serde::{Deserialize, Serialize};
4
use std::fmt;
5
6
// ============================================================================
7
// TILE-001: TcbGeometry Struct
8
// ============================================================================
9
10
/// Dimensions for a Tiling Compute Block
11
///
12
/// Represents the (M, N, K) dimensions of a tile in matrix operations:
13
/// - M: Output rows
14
/// - N: Output columns
15
/// - K: Reduction dimension (inner product)
16
///
17
/// # Alignment Constraints
18
///
19
/// Per the TCB-03 pattern (Tile Quantization Alignment), K must align with
20
/// the quantization superblock size:
21
/// - Q4_0: K % 32 == 0
22
/// - Q4_K: K % 256 == 0
23
/// - Q8_0: K % 32 == 0
24
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25
pub struct TcbGeometry {
26
    /// Items processed in M dimension (rows)
27
    pub m: u32,
28
    /// Items processed in N dimension (columns)
29
    pub n: u32,
30
    /// Reduction dimension (inner product)
31
    pub k: u32,
32
    /// Alignment requirement in bytes (typically 16 for SIMD, 32 for AVX2, 64 for AVX-512)
33
    pub alignment: u32,
34
}
35
36
impl TcbGeometry {
37
    /// Create a new TCB geometry
38
    ///
39
    /// # Panics
40
    /// Panics if any dimension is zero.
41
    #[must_use]
42
0
    pub fn new(m: u32, n: u32, k: u32) -> Self {
43
0
        assert!(m > 0 && n > 0 && k > 0, "TCB dimensions must be non-zero");
44
0
        Self {
45
0
            m,
46
0
            n,
47
0
            k,
48
0
            alignment: 16, // Default to SSE/NEON alignment
49
0
        }
50
0
    }
51
52
    /// Create geometry with explicit alignment
53
    #[must_use]
54
0
    pub fn with_alignment(m: u32, n: u32, k: u32, alignment: u32) -> Self {
55
0
        assert!(m > 0 && n > 0 && k > 0, "TCB dimensions must be non-zero");
56
0
        assert!(
57
0
            alignment.is_power_of_two(),
58
0
            "Alignment must be power of 2"
59
        );
60
0
        Self { m, n, k, alignment }
61
0
    }
62
63
    /// Calculate arithmetic intensity (FLOPS per byte loaded)
64
    ///
65
    /// For GEMM: AI = (2 * M * N * K) / (M*K + K*N) * sizeof(f32)
66
    ///
67
    /// Higher AI means compute-bound; lower means memory-bound.
68
    #[must_use]
69
0
    pub fn arithmetic_intensity(&self) -> f32 {
70
0
        let flops = 2.0 * self.m as f64 * self.n as f64 * self.k as f64;
71
0
        let bytes = (self.m as f64 * self.k as f64 + self.k as f64 * self.n as f64) * 4.0;
72
0
        (flops / bytes) as f32
73
0
    }
74
75
    /// Calculate total elements in the tile
76
    #[must_use]
77
0
    pub fn total_elements(&self) -> u64 {
78
0
        self.m as u64 * self.n as u64
79
0
    }
80
81
    /// Calculate total FLOPs for this tile
82
    #[must_use]
83
0
    pub fn total_flops(&self) -> u64 {
84
0
        2 * self.m as u64 * self.n as u64 * self.k as u64
85
0
    }
86
87
    /// Check if K dimension aligns with Q4_K superblock (256)
88
    #[must_use]
89
0
    pub fn is_q4k_aligned(&self) -> bool {
90
0
        self.k % 256 == 0
91
0
    }
92
93
    /// Check if K dimension aligns with Q4_0/Q8_0 block (32)
94
    #[must_use]
95
0
    pub fn is_q4_0_aligned(&self) -> bool {
96
0
        self.k % 32 == 0
97
0
    }
98
99
    /// Calculate bytes needed for A tile (M × K × sizeof(f32))
100
    #[must_use]
101
0
    pub fn a_tile_bytes(&self) -> usize {
102
0
        self.m as usize * self.k as usize * 4
103
0
    }
104
105
    /// Calculate bytes needed for B tile (K × N × sizeof(f32))
106
    #[must_use]
107
0
    pub fn b_tile_bytes(&self) -> usize {
108
0
        self.k as usize * self.n as usize * 4
109
0
    }
110
111
    /// Calculate bytes needed for C tile (M × N × sizeof(f32))
112
    #[must_use]
113
0
    pub fn c_tile_bytes(&self) -> usize {
114
0
        self.m as usize * self.n as usize * 4
115
0
    }
116
117
    /// Check if tile fits in given cache size (bytes)
118
    #[must_use]
119
0
    pub fn fits_in_cache(&self, cache_bytes: usize) -> bool {
120
0
        self.a_tile_bytes() + self.b_tile_bytes() <= cache_bytes
121
0
    }
122
}
123
124
impl Default for TcbGeometry {
125
0
    fn default() -> Self {
126
        // Sensible default: 4×4 micro-tile for SIMD
127
0
        Self {
128
0
            m: 4,
129
0
            n: 4,
130
0
            k: 4,
131
0
            alignment: 16,
132
0
        }
133
0
    }
134
}
135
136
impl fmt::Display for TcbGeometry {
137
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138
0
        write!(
139
0
            f,
140
0
            "TCB({}×{}×{}, align={}, AI={:.2})",
141
            self.m,
142
            self.n,
143
            self.k,
144
            self.alignment,
145
0
            self.arithmetic_intensity()
146
        )
147
0
    }
148
}
149
// ============================================================================
150
// TILE-001: Tiling Levels
151
// ============================================================================
152
153
/// Tiling hierarchy level
154
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
155
pub enum TcbLevel {
156
    /// Macro-tile: L3 cache / GPU global memory partitioning
157
    Macro,
158
    /// Midi-tile: L2 cache / GPU shared memory
159
    Midi,
160
    /// Micro-tile: Registers / SIMD lanes
161
    Micro,
162
}
163
164
impl TcbLevel {
165
    /// Get typical cache size for this level (x86_64)
166
    #[must_use]
167
0
    pub fn typical_cache_bytes(&self) -> usize {
168
0
        match self {
169
0
            TcbLevel::Macro => 32 * 1024 * 1024, // 32 MB L3
170
0
            TcbLevel::Midi => 256 * 1024,         // 256 KB L2
171
0
            TcbLevel::Micro => 32 * 1024,         // 32 KB L1
172
        }
173
0
    }
174
}