/home/noah/src/trueno/src/backends/q6k/colmajor.rs
Line | Count | Source |
1 | | //! Column-major Q6_K matrix-vector multiplication. |
2 | | //! |
3 | | //! This module implements column-major GEMV for GGML/GGUF format weights, |
4 | | //! where weights are stored column-first for cache-efficient streaming. |
5 | | |
6 | | use super::{f16_to_f32, SUPER_BLOCK_BYTES, SUPER_BLOCK_SIZE}; |
7 | | |
8 | | /// Fused Q6_K matrix-vector multiply for GGML column-major layout |
9 | | /// |
10 | | /// Computes: output = input @ Q6K_weight (GGML convention: y = x @ W) |
11 | | /// where weight is stored in Q6_K format with GGML column-major super-block organization. |
12 | | /// |
13 | | /// # Arguments |
14 | | /// * `q6k_data` - Raw Q6K bytes in GGML column-major layout [ne0, ne1] |
15 | | /// * `input` - F32 input vector [ne1] (input/reduction dimension) |
16 | | /// * `ne0` - Size of output dimension (rows in GGML, output size) |
17 | | /// * `ne1` - Size of input/reduction dimension (columns in GGML, input size) |
18 | | /// |
19 | | /// # Returns |
20 | | /// F32 output vector [ne0] |
21 | 0 | pub fn matmul_q6k_f32_colmajor( |
22 | 0 | q6k_data: &[u8], |
23 | 0 | input: &[f32], |
24 | 0 | ne0: usize, // output dimension (rows) |
25 | 0 | ne1: usize, // input/reduction dimension (columns) |
26 | 0 | ) -> Vec<f32> { |
27 | 0 | assert_eq!(input.len(), ne1, "Input length must match ne1 (input dimension)"); |
28 | | |
29 | | // Number of super-blocks per column (each column has ne0 elements = output_dim) |
30 | 0 | let blocks_per_col = (ne0 + SUPER_BLOCK_SIZE - 1) / SUPER_BLOCK_SIZE; |
31 | 0 | let col_bytes = blocks_per_col * SUPER_BLOCK_BYTES; |
32 | | |
33 | 0 | let mut output = vec![0.0f32; ne0]; |
34 | | |
35 | | // Process each input column and accumulate to outputs |
36 | | // Column j contains weights from input[j] to all ne0 outputs |
37 | 0 | for col_idx in 0..ne1 { |
38 | 0 | let col_start = col_idx * col_bytes; |
39 | 0 | let x_j = input[col_idx]; // Input value for this column |
40 | | |
41 | | // Skip if input is zero (common in sparse activations) |
42 | 0 | if x_j == 0.0 { |
43 | 0 | continue; |
44 | 0 | } |
45 | | |
46 | 0 | for sb_idx in 0..blocks_per_col { |
47 | 0 | let sb_start = col_start + sb_idx * SUPER_BLOCK_BYTES; |
48 | 0 | if sb_start + SUPER_BLOCK_BYTES > q6k_data.len() { |
49 | 0 | break; |
50 | 0 | } |
51 | 0 | let sb_data = &q6k_data[sb_start..sb_start + SUPER_BLOCK_BYTES]; |
52 | | |
53 | 0 | let ql = &sb_data[0..128]; |
54 | 0 | let qh = &sb_data[128..192]; |
55 | 0 | let scales = &sb_data[192..208]; |
56 | 0 | let d = f16_to_f32(u16::from_le_bytes([sb_data[208], sb_data[209]])); |
57 | | |
58 | 0 | let output_offset = sb_idx * SUPER_BLOCK_SIZE; |
59 | | |
60 | 0 | for group in 0..16 { |
61 | 0 | let scale = (scales[group] as i8) as f32; |
62 | 0 | let group_offset = group * 16; |
63 | | |
64 | 0 | for j in 0..16 { |
65 | 0 | let idx = group_offset + j; |
66 | 0 | let output_idx = output_offset + idx; |
67 | 0 | if output_idx >= ne0 { |
68 | 0 | continue; |
69 | 0 | } |
70 | | |
71 | 0 | let ql_byte = ql[idx / 2]; |
72 | 0 | let low4 = if idx % 2 == 0 { |
73 | 0 | ql_byte & 0x0F |
74 | | } else { |
75 | 0 | ql_byte >> 4 |
76 | | }; |
77 | | |
78 | 0 | let qh_byte = qh[idx / 4]; |
79 | 0 | let qh_shift = (idx % 4) * 2; |
80 | 0 | let high2 = (qh_byte >> qh_shift) & 0x03; |
81 | | |
82 | 0 | let q6 = (low4 | (high2 << 4)) as i8 - 32; |
83 | 0 | let dequant = d * scale * q6 as f32; |
84 | 0 | output[output_idx] += x_j * dequant; |
85 | | } |
86 | | } |
87 | | } |
88 | | } |
89 | | |
90 | 0 | output |
91 | 0 | } |
92 | | |
93 | | /// Runtime dispatch for column-major Q6K matmul |
94 | | /// |
95 | | /// Uses scalar implementation for correctness. |
96 | | /// Critical for lm_head which is typically 151936 x 1536 (233M elements). |
97 | | #[inline] |
98 | 0 | pub fn matmul_q6k_f32_colmajor_dispatch( |
99 | 0 | q6k_data: &[u8], |
100 | 0 | input: &[f32], |
101 | 0 | ne0: usize, |
102 | 0 | ne1: usize, |
103 | 0 | ) -> Vec<f32> { |
104 | 0 | matmul_q6k_f32_colmajor(q6k_data, input, ne0, ne1) |
105 | 0 | } |