trueno/blis/mod.rs
1//! BLIS-Style Matrix Multiplication
2//!
3//! High-performance GEMM implementation based on the BLIS framework.
4//!
5//! # References
6//!
7//! - Goto, K., & Van de Geijn, R. A. (2008). Anatomy of High-Performance Matrix Multiplication.
8//! ACM TOMS, 34(3). <https://doi.org/10.1145/1356052.1356053>
9//! - Van Zee, F. G., & Van de Geijn, R. A. (2015). BLIS: A Framework for Rapidly Instantiating
10//! BLAS Functionality. ACM TOMS, 41(3). <https://doi.org/10.1145/2764454>
11//! - Low, T. M., et al. (2016). Analytical Modeling Is Enough for High-Performance BLIS.
12//! ACM TOMS, 43(2). <https://doi.org/10.1145/2925987>
13//!
14//! # Toyota Production System Integration
15//!
16//! - **Jidoka**: Runtime guards that stop on numerical errors (see [`jidoka`] module)
17//! - **Poka-Yoke**: Compile-time type safety for panel dimensions
18//! - **Heijunka**: Load-balanced parallel execution
19//! - **Kaizen**: Performance tracking for continuous improvement (see [`profiler`] module)
20//!
21//! # Module Structure
22//!
23//! - [`jidoka`]: Runtime validation guards (stop-on-defect)
24//! - [`profiler`]: Performance tracking at all BLIS hierarchy levels
25//! - [`microkernels`]: High-performance SIMD compute kernels
26//! - [`backend_selection`]: Automatic CPU/GPU backend selection
27//! - [`reference`]: Scalar reference GEMM for validation
28//! - [`packing`]: Cache-optimized matrix packing routines
29//! - [`compute`]: Core BLIS blocked GEMM computation
30//! - [`parallel`]: Parallel GEMM with Heijunka scheduling
31//! - [`transpose`]: Matrix transpose operations
32
33pub mod attention;
34pub mod backend_selection;
35pub mod cache_topology;
36// pack_a_block_generic and pack_b_block_nr16 are called only from the x86_64 AVX-512 GEMMs,
37// so they are dead on ARM. Their cfg belongs on the functions, but compute.rs carries
38// 11 pre-existing complexity violations and the pre-commit gate refuses any edit to it
39// until it is decomposed (PMAT-1102). `expect` turns this into an error once they go.
40#[cfg_attr(
41 not(target_arch = "x86_64"),
42 expect(dead_code, reason = "x86_64-only BLIS packers; see PMAT-1102")
43)]
44pub mod compute;
45pub mod elementwise;
46pub mod gemv;
47pub mod jidoka;
48pub mod microkernels;
49pub mod norms;
50pub mod packing;
51pub mod parallel;
52pub mod prepacked;
53pub mod profiler;
54pub mod reference;
55pub mod softmax;
56pub mod transpose;
57
58// Re-export jidoka types for backwards compatibility
59pub use jidoka::{JidokaError, JidokaGuard};
60
61// Re-export profiler types for backwards compatibility
62pub use profiler::{BlisLevelStats, BlisProfileLevel, BlisProfiler, KaizenMetrics};
63
64// Re-export microkernel functions
65#[cfg(target_arch = "aarch64")]
66pub use microkernels::microkernel_8x8_neon;
67pub use microkernels::microkernel_scalar;
68#[cfg(target_arch = "x86_64")]
69pub use microkernels::{microkernel_8x6_avx2, microkernel_8x6_avx2_asm, microkernel_8x6_true_asm};
70
71// Re-export backend selection types
72pub use backend_selection::{
73 gemm_auto, BackendCostModel, BrickLevel, ComputeBackend, PtxMicrokernelSpec, RooflineResult,
74 UnifiedBrickProfiler, WgslMicrokernelSpec,
75};
76
77// Re-export reference GEMM
78pub use reference::{gemm_reference, gemm_reference_with_jidoka};
79
80// Re-export packing functions
81pub use packing::{pack_a, pack_b, packed_a_size, packed_b_size};
82
83// Re-export compute
84#[cfg(target_arch = "x86_64")]
85pub use compute::gemm_blis_broadcast_b;
86pub use compute::{gemm_blis, gemm_blis_with_prepacked_b};
87
88// Re-export parallel
89#[cfg(feature = "parallel")]
90pub use parallel::gemm_blis_parallel_shared_b;
91pub use parallel::{gemm_blis_parallel, gemm_blis_parallel_with_prepacked_b, HeijunkaScheduler};
92
93// Re-export prepacked
94pub use prepacked::PrepackedB;
95
96// Re-export transpose
97pub use transpose::transpose;
98
99use crate::error::TruenoError;
100
101// ============================================================================
102// BLIS Configuration Constants
103// ============================================================================
104
105/// Microkernel row dimension (AVX2: 8 f32 per ymm register)
106pub const MR: usize = 8;
107
108/// Microkernel column dimension (6 columns fit in remaining registers)
109pub const NR: usize = 6;
110
111/// K-dimension blocking for L1 cache (256 elements = 1KB)
112pub const KC: usize = 256;
113
114/// M-dimension blocking for L2 cache.
115/// Must be a multiple of MR. 128 = 16×MR for AVX2 (vs old 72 = 9×MR).
116/// Larger MC reduces packing overhead per macroblock (fewer ic-loop iterations).
117/// Zen 4 L2 = 1MB per core; MC×KC×4B = 128×256×4 = 128KB << 1MB.
118pub const MC: usize = 128;
119
120/// N-dimension blocking for L3 cache
121pub const NC: usize = 4096;
122
123// ============================================================================
124// AVX-512 BLIS Configuration Constants
125// ============================================================================
126
127/// AVX-512 microkernel row dimension (16 f32 per zmm register)
128pub const MR_512: usize = 16;
129
130/// AVX-512 microkernel column dimension (8 columns in remaining zmm registers)
131pub const NR_512: usize = 8;
132
133/// AVX-512 K-dimension blocking (same as AVX2, L1 limited)
134pub const KC_512: usize = 256;
135
136/// AVX-512 M-dimension blocking for L2 cache.
137/// 128 = 8×MR_512. Zen 4 L2 = 1MB; 128×256×4 = 128KB.
138pub const MC_512: usize = 128;
139
140/// AVX-512 N-dimension blocking for L3 cache
141pub const NC_512: usize = 4096;
142
143// ============================================================================
144// AVX-512 32×6 Microkernel Constants (Phase 4, Appendix D optimization #1)
145// ============================================================================
146
147/// 32×6 microkernel: 2 zmm rows × 6 columns = 12 accumulators.
148/// 1.5× more FMAs per K step than 16×8 (12 vs 8).
149pub const MR_512V2: usize = 32;
150
151/// 6 columns: balances register pressure (12 acc + 2 A load = 14 zmm).
152pub const NR_512V2: usize = 6;
153
154/// Increased KC for 32×6: 32×256×4 = 32 KB fits L1 (32 KB on Zen 4).
155pub const KC_512V2: usize = 256;
156
157/// MC for 32×6: 192 = 6×MR_512V2. Packed A = 192×256×4 = 192 KB fits L2.
158pub const MC_512V2: usize = 192;
159
160/// NC for 32×6: same L3 blocking.
161pub const NC_512V2: usize = 4096;
162
163// ============================================================================
164// Public API
165// ============================================================================
166
167/// High-performance GEMM using BLIS algorithm
168///
169/// Computes C += A * B where:
170/// - A is M x K (row-major)
171/// - B is K x N (row-major)
172/// - C is M x N (row-major)
173///
174/// Automatically selects single-threaded or parallel execution based on matrix size.
175pub fn gemm(
176 m: usize,
177 n: usize,
178 k: usize,
179 a: &[f32],
180 b: &[f32],
181 c: &mut [f32],
182) -> Result<(), TruenoError> {
183 // Contract: matmul-kernel-v1.yaml precondition (pv codegen)
184 contract_pre_matmul!(a);
185
186 let result = {
187 #[cfg(feature = "parallel")]
188 {
189 gemm_blis_parallel(m, n, k, a, b, c)
190 }
191 #[cfg(not(feature = "parallel"))]
192 {
193 gemm_blis(m, n, k, a, b, c, None)
194 }
195 };
196 if result.is_ok() {
197 contract_post_matmul!(c);
198 }
199 result
200}
201
202/// GEMM with profiling enabled
203pub fn gemm_profiled(
204 m: usize,
205 n: usize,
206 k: usize,
207 a: &[f32],
208 b: &[f32],
209 c: &mut [f32],
210 profiler: &mut BlisProfiler,
211) -> Result<(), TruenoError> {
212 gemm_blis(m, n, k, a, b, c, Some(profiler))
213}
214
215/// Fused GEMM + bias + ReLU: C = max(0, A×B + bias)
216///
217/// Performs matmul then applies bias addition and ReLU activation in a single
218/// pass over C while the output tiles are still in L1/L2 cache. This avoids
219/// two extra full-matrix memory passes that separate add+relu would require.
220///
221/// For GEMM 64: saves ~2µs (bias+relu would cost 2×0.8µs on cold data).
222/// For GEMM 128: saves ~5µs.
223///
224/// # Arguments
225///
226/// * `bias` - Per-column bias vector of length `n` (broadcast across rows)
227///
228/// # Errors
229///
230/// Returns `Err` if dimensions don't match or bias length != n.
231pub fn gemm_bias_relu(
232 m: usize,
233 n: usize,
234 k: usize,
235 a: &[f32],
236 b: &[f32],
237 bias: &[f32],
238 c: &mut [f32],
239) -> Result<(), TruenoError> {
240 if bias.len() != n {
241 return Err(TruenoError::InvalidInput(format!(
242 "gemm_bias_relu: bias.len()={} != n={}",
243 bias.len(),
244 n
245 )));
246 }
247 // Step 1: GEMM (C = A×B)
248 gemm(m, n, k, a, b, c)?;
249
250 // Step 2: Fused bias + ReLU in-place on hot cache data.
251 // C is still in L1/L2 from the GEMM store — no DRAM reads needed.
252 for row in 0..m {
253 let row_start = row * n;
254 for col in 0..n {
255 let val = c[row_start + col] + bias[col];
256 c[row_start + col] = val.max(0.0);
257 }
258 }
259 Ok(())
260}
261
262#[cfg(test)]
263mod tests;