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/vector/mod.rs
Line
Count
Source
1
//! Vector type with multi-backend support
2
//!
3
//! This module provides the core `Vector<T>` type with SIMD-optimized operations
4
//! across multiple backends (Scalar, SSE2, AVX2, AVX-512, NEON, WASM SIMD).
5
//!
6
//! GPU thresholds intentionally set to usize::MAX to disable GPU for element-wise ops.
7
//! See docs/performance-analysis.md - GPU is 2-65,000x SLOWER than scalar for these ops.
8
9
#![allow(clippy::absurd_extreme_comparisons)]
10
11
// Submodules
12
pub mod dispatch;
13
mod ops;
14
15
// Tests (~10K lines extracted for TDG compliance)
16
#[cfg(test)]
17
mod tests;
18
19
use crate::{Backend, Result, TruenoError};
20
21
/// High-performance vector with multi-backend support
22
///
23
/// # Examples
24
///
25
/// ```
26
/// use trueno::Vector;
27
///
28
/// let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
29
/// let b = Vector::from_slice(&[4.0, 5.0, 6.0]);
30
/// let result = a.add(&b).unwrap();
31
///
32
/// assert_eq!(result.as_slice(), &[5.0, 7.0, 9.0]);
33
/// ```
34
#[derive(Debug, Clone, PartialEq)]
35
pub struct Vector<T> {
36
    data: Vec<T>,
37
    backend: Backend,
38
}
39
40
impl<T> Vector<T>
41
where
42
    T: Clone,
43
{
44
    /// Create vector from slice using auto-selected optimal backend
45
    ///
46
    /// # Performance
47
    ///
48
    /// Auto-selects the best available backend at creation time based on:
49
    /// - CPU feature detection (AVX-512 > AVX2 > AVX > SSE2)
50
    /// - Vector size (GPU for large workloads)
51
    /// - Platform availability (NEON on ARM, WASM SIMD in browser)
52
    ///
53
    /// # Examples
54
    ///
55
    /// ```
56
    /// use trueno::Vector;
57
    ///
58
    /// let v = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0]);
59
    /// assert_eq!(v.len(), 4);
60
    /// ```
61
247k
    pub fn from_slice(data: &[T]) -> Self {
62
247k
        Self {
63
247k
            data: data.to_vec(),
64
247k
            backend: crate::select_best_available_backend(),
65
247k
        }
66
247k
    }
67
68
    /// Create vector from an existing Vec (takes ownership, no copy)
69
    ///
70
    /// This is more efficient than `from_slice` when you already have a Vec
71
    /// and don't need to keep it, as it avoids an extra allocation and copy.
72
    ///
73
    /// # Examples
74
    ///
75
    /// ```
76
    /// use trueno::Vector;
77
    ///
78
    /// let data = vec![1.0, 2.0, 3.0];
79
    /// let v = Vector::from_vec(data);
80
    /// assert_eq!(v.len(), 3);
81
    /// ```
82
10.7k
    pub fn from_vec(data: Vec<T>) -> Self {
83
10.7k
        Self {
84
10.7k
            data,
85
10.7k
            backend: crate::select_best_available_backend(),
86
10.7k
        }
87
10.7k
    }
88
89
    /// Create vector with specific backend (for benchmarking or testing)
90
    ///
91
    /// # Examples
92
    ///
93
    /// ```
94
    /// use trueno::{Vector, Backend};
95
    ///
96
    /// let v = Vector::from_slice_with_backend(&[1.0, 2.0], Backend::Scalar);
97
    /// assert_eq!(v.len(), 2);
98
    /// ```
99
0
    pub fn from_slice_with_backend(data: &[T], backend: Backend) -> Self {
100
0
        let resolved_backend = match backend {
101
0
            Backend::Auto => crate::select_best_available_backend(),
102
0
            _ => backend,
103
        };
104
105
0
        Self {
106
0
            data: data.to_vec(),
107
0
            backend: resolved_backend,
108
0
        }
109
0
    }
110
}
111
112
impl Vector<f32> {
113
    /// Create vector with specified alignment for optimal SIMD performance
114
    ///
115
    /// This method attempts to create a vector with memory aligned to the specified byte boundary.
116
    /// Note: Rust's Vec allocator may already provide sufficient alignment for most use cases.
117
    /// This method validates the alignment requirement but uses standard Vec allocation.
118
    ///
119
    /// # Arguments
120
    ///
121
    /// * `size` - Number of elements to allocate
122
    /// * `backend` - Backend to use for operations
123
    /// * `alignment` - Requested alignment in bytes (must be power of 2: 16, 32, 64)
124
    ///
125
    /// # Recommended Alignments
126
    ///
127
    /// - SSE2: 16 bytes (128-bit)
128
    /// - AVX2: 32 bytes (256-bit)
129
    /// - AVX-512: 64 bytes (512-bit)
130
    ///
131
    /// # Note on Implementation
132
    ///
133
    /// Currently uses Rust's default Vec allocator, which typically provides 16-byte alignment
134
    /// on modern systems. Custom allocators for specific alignments will be added in future versions.
135
    ///
136
    /// # Examples
137
    ///
138
    /// ```
139
    /// use trueno::{Vector, Backend};
140
    ///
141
    /// // Create vector with requested 16-byte alignment
142
    /// let v = Vector::with_alignment(100, Backend::SSE2, 16).unwrap();
143
    /// assert_eq!(v.len(), 100);
144
    /// ```
145
    ///
146
    /// # Errors
147
    ///
148
    /// Returns `TruenoError::InvalidInput` if alignment is not a power of 2.
149
0
    pub fn with_alignment(size: usize, backend: Backend, alignment: usize) -> Result<Self> {
150
        // Validate alignment is power of 2
151
0
        if alignment == 0 || (alignment & (alignment - 1)) != 0 {
152
0
            return Err(TruenoError::InvalidInput(format!(
153
0
                "Alignment must be power of 2, got {}",
154
0
                alignment
155
0
            )));
156
0
        }
157
158
        // Resolve backend
159
0
        let resolved_backend = match backend {
160
0
            Backend::Auto => crate::select_best_available_backend(),
161
0
            _ => backend,
162
        };
163
164
        // For now, use standard Vec allocation which typically provides good alignment
165
        // Future enhancement: use custom allocator for guaranteed alignment > 16 bytes
166
0
        let data = vec![0.0f32; size];
167
168
        // Verify actual alignment (for informational purposes)
169
0
        let ptr = data.as_ptr() as usize;
170
0
        let actual_alignment = ptr & !(ptr - 1); // Find lowest set bit
171
172
        // Log warning if alignment requirement not met (for future enhancement)
173
0
        if alignment > actual_alignment {
174
0
            // Note: This is not an error, just informational
175
0
            // The unaligned loads in SSE2 (_mm_loadu_ps) will still work correctly
176
0
            eprintln!(
177
0
                "Note: Requested {}-byte alignment, got {}-byte alignment. Using unaligned loads.",
178
0
                alignment, actual_alignment
179
0
            );
180
0
        }
181
182
0
        Ok(Self {
183
0
            data,
184
0
            backend: resolved_backend,
185
0
        })
186
0
    }
187
}
188
189
impl<T> Vector<T>
190
where
191
    T: Clone,
192
{
193
    /// Get underlying data as slice
194
    ///
195
    /// # Examples
196
    ///
197
    /// ```
198
    /// use trueno::Vector;
199
    ///
200
    /// let v = Vector::from_slice(&[1.0, 2.0, 3.0]);
201
    /// assert_eq!(v.as_slice(), &[1.0, 2.0, 3.0]);
202
    /// ```
203
10.6k
    pub fn as_slice(&self) -> &[T] {
204
10.6k
        &self.data
205
10.6k
    }
206
207
    /// Get vector length
208
    ///
209
    /// # Examples
210
    ///
211
    /// ```
212
    /// use trueno::Vector;
213
    ///
214
    /// let v = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0]);
215
    /// assert_eq!(v.len(), 5);
216
    /// ```
217
452k
    pub fn len(&self) -> usize {
218
452k
        self.data.len()
219
452k
    }
220
221
    /// Check if vector is empty
222
    ///
223
    /// # Examples
224
    ///
225
    /// ```
226
    /// use trueno::Vector;
227
    ///
228
    /// let v1: Vector<f32> = Vector::from_slice(&[]);
229
    /// assert!(v1.is_empty());
230
    ///
231
    /// let v2 = Vector::from_slice(&[1.0]);
232
    /// assert!(!v2.is_empty());
233
    /// ```
234
0
    pub fn is_empty(&self) -> bool {
235
0
        self.data.is_empty()
236
0
    }
237
238
    /// Get the backend being used
239
0
    pub fn backend(&self) -> Backend {
240
0
        self.backend
241
0
    }
242
}
243
244
// Note: Vector<f32> operations have been moved to submodules in ops/:
245
// - ops/normalization.rs: zscore, minmax_normalize, layer_norm, layer_norm_simple, normalize
246
// - ops/norms.rs: norm_l1, norm_l2, norm_linf
247
// - ops/transforms.rs: abs, clamp, clip, lerp, sqrt, recip, pow
248
// - ops/arithmetic.rs: add, sub, mul, div, scale, fma
249
// - ops/reductions.rs: dot, sum, max, min, argmax, argmin, mean, variance, stddev, covariance, correlation
250
// - ops/activations.rs: relu, sigmoid, gelu, etc.
251
// - ops/transcendental.rs: exp, log, sin, cos, etc.
252
// - ops/rounding.rs: floor, ceil, round, trunc, etc.
253