/home/noah/src/realizar/src/gpu/simd_ops.rs
Line | Count | Source |
1 | | //! GPU SIMD Operations Module (PMAT-802) |
2 | | //! |
3 | | //! Extracted from gpu/mod.rs - SIMD-accelerated compute primitives. |
4 | | //! |
5 | | //! ## Contents |
6 | | //! - `scalar_softmax`, `simd_softmax` - Softmax implementations (IMP-038) |
7 | | //! - `scalar_rope`, `simd_rope` - RoPE implementations (IMP-041) |
8 | | |
9 | | // ============================================================================ |
10 | | // SIMD-accelerated operations (M18 - IMP-038) |
11 | | // ============================================================================ |
12 | | |
13 | | /// Scalar softmax implementation (baseline for comparison) |
14 | | /// |
15 | | /// Computes softmax using standard scalar operations. |
16 | | #[must_use] |
17 | 127 | pub fn scalar_softmax(input: &[f32]) -> Vec<f32> { |
18 | 127 | if input.is_empty() { |
19 | 4 | return Vec::new(); |
20 | 123 | } |
21 | | |
22 | | // Find max for numerical stability |
23 | 123 | let max_val = input.iter().copied().fold(f32::NEG_INFINITY, f32::max); |
24 | | |
25 | | // Compute exp(x - max) and sum |
26 | 112k | let exp_vals123 : Vec<f32>123 = input123 .iter123 ().map123 (|&x| (x - max_val).exp()).collect123 (); |
27 | 123 | let sum: f32 = exp_vals.iter().sum(); |
28 | | |
29 | | // Normalize |
30 | 112k | exp_vals.iter()123 .map123 (|&e| e / sum).collect123 () |
31 | 127 | } |
32 | | |
33 | | /// SIMD-accelerated softmax implementation (M18 - IMP-038) |
34 | | /// |
35 | | /// Uses Trueno's SIMD operations for vectorized computation. |
36 | | /// Falls back to scalar for unsupported sizes. |
37 | | #[must_use] |
38 | 122 | pub fn simd_softmax(input: &[f32]) -> Vec<f32> { |
39 | 122 | if input.is_empty() { |
40 | 4 | return Vec::new(); |
41 | 118 | } |
42 | | |
43 | | // Find max using SIMD via trueno |
44 | 118 | let max_val = input.iter().copied().fold(f32::NEG_INFINITY, f32::max); |
45 | | |
46 | | // Compute exp(x - max) - exp is not SIMD accelerated |
47 | 112k | let exp_vals118 : Vec<f32>118 = input118 .iter118 ().map118 (|&x| (x - max_val).exp()).collect118 (); |
48 | | |
49 | | // Sum using trueno's SIMD sum |
50 | 118 | let exp_vec = trueno::Vector::from_slice(&exp_vals); |
51 | 118 | let sum = exp_vec.sum().unwrap_or_else(|_| exp_vals.iter()0 .sum0 ()); |
52 | | |
53 | | // Normalize |
54 | 112k | exp_vals.iter()118 .map118 (|&e| e / sum).collect118 () |
55 | 122 | } |
56 | | |
57 | | // ============================================================================ |
58 | | // Scalar and SIMD RoPE implementations (M19 - IMP-041) |
59 | | // ============================================================================ |
60 | | |
61 | | /// Scalar RoPE (Rotary Position Embedding) implementation |
62 | | /// |
63 | | /// Standard scalar implementation of rotary position embeddings. |
64 | | /// Input shape: [seq_len * hidden_dim] flattened |
65 | | #[must_use] |
66 | 21 | pub fn scalar_rope(input: &[f32], seq_len: usize, head_dim: usize, theta: f32) -> Vec<f32> { |
67 | 21 | if input.is_empty() || seq_len == 017 || head_dim == 014 { |
68 | 11 | return Vec::new(); |
69 | 10 | } |
70 | | |
71 | 10 | let hidden_dim = input.len() / seq_len; |
72 | 10 | let num_heads = hidden_dim / head_dim; |
73 | 10 | let mut output = vec![0.0f32; input.len()]; |
74 | | |
75 | | // Compute RoPE for each position |
76 | 20 | for pos in 0..seq_len10 { |
77 | 22 | for head in 0..num_heads20 { |
78 | 22 | let head_start = pos * hidden_dim + head * head_dim; |
79 | | |
80 | | // Apply rotary embedding to pairs of elements |
81 | 134 | for i in 0..head_dim / 222 { |
82 | 134 | let freq = 1.0 / theta.powf((2.0 * i as f32) / head_dim as f32); |
83 | 134 | let angle = pos as f32 * freq; |
84 | 134 | let cos_val = angle.cos(); |
85 | 134 | let sin_val = angle.sin(); |
86 | | |
87 | 134 | let idx0 = head_start + i; |
88 | 134 | let idx1 = head_start + i + head_dim / 2; |
89 | | |
90 | 134 | if idx1 < input.len() { |
91 | 134 | let x0 = input[idx0]; |
92 | 134 | let x1 = input[idx1]; |
93 | 134 | output[idx0] = x0 * cos_val - x1 * sin_val; |
94 | 134 | output[idx1] = x0 * sin_val + x1 * cos_val; |
95 | 134 | }0 |
96 | | } |
97 | | } |
98 | | } |
99 | | |
100 | 10 | output |
101 | 21 | } |
102 | | |
103 | | /// SIMD-accelerated RoPE implementation (M19 - IMP-041) |
104 | | /// |
105 | | /// Uses Trueno's SIMD operations for vectorized position encoding. |
106 | | #[must_use] |
107 | 14 | pub fn simd_rope(input: &[f32], seq_len: usize, head_dim: usize, theta: f32) -> Vec<f32> { |
108 | 14 | if input.is_empty() || seq_len == 010 || head_dim == 08 { |
109 | 8 | return Vec::new(); |
110 | 6 | } |
111 | | |
112 | 6 | let hidden_dim = input.len() / seq_len; |
113 | 6 | let num_heads = hidden_dim / head_dim; |
114 | 6 | let half_head = head_dim / 2; |
115 | | |
116 | | // Pre-compute frequency table (cache-friendly) |
117 | 6 | let mut freqs: Vec<f32> = Vec::with_capacity(half_head); |
118 | 36 | for i in 0..half_head6 { |
119 | 36 | freqs.push(1.0 / theta.powf((2.0 * i as f32) / head_dim as f32)); |
120 | 36 | } |
121 | | |
122 | 6 | let mut output = vec![0.0f32; input.len()]; |
123 | | |
124 | | // Process each position using SIMD operations |
125 | 13 | for pos in 0..seq_len6 { |
126 | | // Pre-compute angles for this position |
127 | 80 | let angles13 : Vec<f32>13 = freqs.iter()13 .map13 (|&f| pos as f32 * f).collect13 (); |
128 | 80 | let cos_vals13 : Vec<f32>13 = angles.iter()13 .map13 (|&a| a.cos()).collect13 (); |
129 | 80 | let sin_vals13 : Vec<f32>13 = angles.iter()13 .map13 (|&a| a.sin()).collect13 (); |
130 | | |
131 | | // Use trueno vectors for batch operations |
132 | 13 | let cos_vec = trueno::Vector::from_slice(&cos_vals); |
133 | 13 | let sin_vec = trueno::Vector::from_slice(&sin_vals); |
134 | | |
135 | 15 | for head in 0..num_heads13 { |
136 | 15 | let head_start = pos * hidden_dim + head * head_dim; |
137 | | |
138 | | // Extract x0 and x1 halves |
139 | 15 | let x0_slice = &input[head_start..head_start + half_head]; |
140 | 15 | let x1_slice = &input[head_start + half_head..head_start + head_dim]; |
141 | | |
142 | 15 | let x0_vec = trueno::Vector::from_slice(x0_slice); |
143 | 15 | let x1_vec = trueno::Vector::from_slice(x1_slice); |
144 | | |
145 | | // Compute: out0 = x0 * cos - x1 * sin |
146 | | // out1 = x0 * sin + x1 * cos |
147 | 15 | let x0_cos = x0_vec.mul(&cos_vec).unwrap_or_else(|_| x0_vec0 .clone0 ()); |
148 | 15 | let x1_sin = x1_vec.mul(&sin_vec).unwrap_or_else(|_| x1_vec0 .clone0 ()); |
149 | 15 | let x0_sin = x0_vec.mul(&sin_vec).unwrap_or_else(|_| x0_vec0 .clone0 ()); |
150 | 15 | let x1_cos = x1_vec.mul(&cos_vec).unwrap_or_else(|_| x1_vec0 .clone0 ()); |
151 | | |
152 | 15 | let out0 = x0_cos |
153 | 15 | .sub(&x1_sin) |
154 | 15 | .unwrap_or_else(|_| trueno::Vector::from_slice0 (x0_slice0 )); |
155 | 15 | let out1 = x0_sin |
156 | 15 | .add(&x1_cos) |
157 | 15 | .unwrap_or_else(|_| trueno::Vector::from_slice0 (x1_slice0 )); |
158 | | |
159 | | // Copy results to output |
160 | 15 | output[head_start..head_start + half_head].copy_from_slice(out0.as_slice()); |
161 | 15 | output[head_start + half_head..head_start + head_dim].copy_from_slice(out1.as_slice()); |
162 | | } |
163 | | } |
164 | | |
165 | 6 | output |
166 | 14 | } |