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/lib.rs
Line
Count
Source
1
// ============================================================================
2
// Development-phase lint allows - to be addressed incrementally
3
// ============================================================================
4
// Allow manual_div_ceil - clearer for block calculations
5
#![allow(clippy::manual_div_ceil)]
6
// Allow manual_is_multiple_of - clearer alignment checks
7
#![allow(clippy::manual_is_multiple_of)]
8
// Allow needless_range_loop - index access is clearer in some SIMD algorithms
9
#![allow(clippy::needless_range_loop)]
10
// Allow empty line after doc comments - formatting preference
11
#![allow(clippy::empty_line_after_doc_comments)]
12
// Allow similar names - semantic distinction is clear
13
#![allow(clippy::similar_names)]
14
// Allow many single char names - standard math/matrix notation
15
#![allow(clippy::many_single_char_names)]
16
// Allow too many arguments - SIMD/compute APIs require many parameters
17
#![allow(clippy::too_many_arguments)]
18
// Allow type complexity - complex SIMD types
19
#![allow(clippy::type_complexity)]
20
// Allow macro metavars in unsafe - necessary for SIMD dispatch macros
21
#![allow(clippy::macro_metavars_in_unsafe)]
22
// Allow missing panics doc - will be added incrementally
23
#![allow(clippy::missing_panics_doc)]
24
// Allow missing errors doc - will be added incrementally
25
#![allow(clippy::missing_errors_doc)]
26
// Allow missing safety doc - will be added incrementally
27
#![allow(clippy::missing_safety_doc)]
28
// Allow excessive precision - SIMD math constants need specific precision
29
#![allow(clippy::excessive_precision)]
30
// Allow unnecessary cast - clearer type annotations in some cases
31
#![allow(clippy::unnecessary_cast)]
32
// Allow cast_possible_truncation - handled in SIMD code
33
#![allow(clippy::cast_possible_truncation)]
34
// Allow cast_sign_loss - handled in SIMD code
35
#![allow(clippy::cast_sign_loss)]
36
// Allow cast_precision_loss - handled in SIMD code
37
#![allow(clippy::cast_precision_loss)]
38
39
//! Trueno: Multi-Target High-Performance Compute Library
40
//!
41
//! **Trueno** (Spanish: "thunder") provides unified, high-performance compute primitives
42
//! across three execution targets:
43
//!
44
//! 1. **CPU SIMD** - x86 (SSE2/AVX/AVX2/AVX-512), ARM (NEON), WASM (SIMD128)
45
//! 2. **GPU** - Vulkan/Metal/DX12/WebGPU via `wgpu`
46
//! 3. **WebAssembly** - Portable SIMD128 for browser/edge deployment
47
//!
48
//! # Design Principles
49
//!
50
//! - **Write once, optimize everywhere**: Single algorithm, multiple backends
51
//! - **Runtime dispatch**: Auto-select best implementation based on CPU features
52
//! - **Zero unsafe in public API**: Safety via type system, `unsafe` isolated in backends
53
//! - **Benchmarked performance**: Every optimization must prove ≥10% speedup
54
//! - **Extreme TDD**: >90% test coverage, mutation testing, property-based tests
55
//!
56
//! # Quick Start
57
//!
58
//! ```rust
59
//! use trueno::Vector;
60
//!
61
//! let a = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0]);
62
//! let b = Vector::from_slice(&[5.0, 6.0, 7.0, 8.0]);
63
//!
64
//! // Auto-selects best backend (AVX2/GPU/WASM)
65
//! let result = a.add(&b).unwrap();
66
//! assert_eq!(result.as_slice(), &[6.0, 8.0, 10.0, 12.0]);
67
//! ```
68
69
pub mod backends;
70
pub mod blis;
71
pub mod brick;
72
pub mod chaos;
73
pub mod eigen;
74
pub mod error;
75
pub mod hardware;
76
pub mod hash;
77
pub mod matrix;
78
pub mod monitor;
79
pub mod simulation;
80
pub mod tiling;
81
pub mod tuner;
82
pub mod vector;
83
84
pub use eigen::SymmetricEigen;
85
pub use error::{Result, TruenoError};
86
pub use hash::{hash_bytes, hash_key, hash_keys_batch, hash_keys_batch_with_backend};
87
pub use matrix::Matrix;
88
pub use monitor::{
89
    cuda_monitor_available, GpuBackend, GpuClockMetrics, GpuDeviceInfo, GpuMemoryMetrics,
90
    GpuMetrics, GpuMonitor, GpuPcieMetrics, GpuPowerMetrics, GpuThermalMetrics, GpuUtilization,
91
    GpuVendor, MonitorConfig, MonitorError,
92
};
93
#[cfg(feature = "cuda-monitor")]
94
pub use monitor::{enumerate_cuda_devices, query_cuda_device_info, query_cuda_memory};
95
pub use vector::Vector;
96
97
// ComputeBrick exports
98
pub use brick::{
99
    AddOp, AssertionResult, AttentionOp, BrickError, BrickLayer, BrickProfiler, BrickSample,
100
    BrickStats, BrickTimer, BrickVerification, ByteBudget, ComputeAssertion, ComputeBackend,
101
    ComputeBrick, ComputeOp, DotOp, FusedGateUpOp, FusedGateUpWeights, FusedQKVOp,
102
    FusedQKVWeights, MatmulOp, SoftmaxOp, TokenBudget, TokenResult,
103
    // QUANT-Q5K: Q5_K and Q6_K quantization formats (llama.cpp compatible)
104
    BlockQ5K, BlockQ6K, DotQ5KOp, DotQ6KOp,
105
    // CORRECTNESS-011: Divergence detection types
106
    KernelChecksum, DivergenceInfo, fnv1a_f32_checksum,
107
    // PAR-200: BrickProfiler v2 types
108
    BrickId, BrickIdTimer, BrickCategory, BrickBottleneck, CategoryStats, SyncMode,
109
    // PAR-201: Execution path graph types
110
    ExecutionNodeId, ExecutionNode, EdgeType, ExecutionEdge, ExecutionGraph, PtxRegistry,
111
    // TILING-SPEC-001: Tile-level profiling types
112
    TileLevel, TileStats, TileTimer,
113
};
114
115
// Hardware capability exports (PMAT-447)
116
pub use hardware::{
117
    Bottleneck, CpuCapability, GpuCapability, HardwareCapability, RooflineParams, SimdWidth,
118
    GpuBackend as HardwareGpuBackend, default_hardware_path,
119
};
120
121
// ML Tuner exports (T-TUNER-003 through T-TUNER-007, GH#80-84)
122
pub use tuner::{
123
    BottleneckClass, BottleneckPrediction, BrickTuner, ConceptDriftStatus, ExperimentSuggestion,
124
    FeatureExtractor, KernelClassifier, KernelRecommendation, KernelType, QuantType, RunConfig,
125
    ThroughputPrediction, ThroughputRegressor, TrainingSample, TrainingStats, TunerDataCollector,
126
    TunerError, TunerFeatures, TunerRecommendation, UserFeedback,
127
};
128
129
// Tiling Compute Blocks exports (TILING-SPEC-001)
130
pub use tiling::{
131
    PackingLayout, PrefetchLocality, TcbGeometry, TcbIndexCalculator, TcbLevel, TilingBackend,
132
    TilingConfig, TilingError, TilingStats, TiledQ4KMatvec,
133
    Q4K_SUPERBLOCK_SIZE, Q4K_SUPERBLOCK_BYTES,
134
    optimal_prefetch_distance, pack_a_index, pack_b_index, swizzle_index,
135
};
136
137
/// Backend execution target
138
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139
pub enum Backend {
140
    /// Scalar fallback (no SIMD)
141
    Scalar,
142
    /// SSE2 (x86_64 baseline)
143
    SSE2,
144
    /// AVX (256-bit)
145
    AVX,
146
    /// AVX2 (256-bit with FMA)
147
    AVX2,
148
    /// AVX-512 (512-bit)
149
    AVX512,
150
    /// ARM NEON
151
    NEON,
152
    /// WebAssembly SIMD128
153
    WasmSIMD,
154
    /// GPU compute (wgpu)
155
    GPU,
156
    /// Auto-select best available
157
    Auto,
158
}
159
160
impl Backend {
161
    /// Select the best available backend for the current platform
162
    ///
163
    /// This is a convenience wrapper around `select_best_available_backend()`
164
10
    pub fn select_best() -> Self {
165
10
        select_best_available_backend()
166
10
    }
167
}
168
169
/// Operation complexity for GPU dispatch eligibility
170
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
171
pub enum OpComplexity {
172
    /// Simple operations (add, mul) - prefer SIMD unless very large
173
    Low = 0,
174
    /// Moderate operations (dot, reduce) - GPU beneficial at 100K+
175
    Medium = 1,
176
    /// Complex operations (matmul, convolution) - GPU beneficial at 10K+
177
    High = 2,
178
}
179
180
/// Operation type for SIMD backend selection
181
///
182
/// Based on AVX-512 performance analysis (see AVX512_ANALYSIS.md), operations are
183
/// categorized by their memory vs compute characteristics to guide optimal backend selection.
184
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185
pub enum OperationType {
186
    /// Memory-bound operations (add, sub, mul, scale, div)
187
    ///
188
    /// These operations perform minimal computation per memory access (arithmetic intensity < 1 op/byte).
189
    /// Prefer AVX2 over AVX-512 due to memory bandwidth bottleneck.
190
    ///
191
    /// AVX-512 performance: 0.67-1.20x scalar (often slower!)
192
    /// AVX2 performance: 1.0-1.2x scalar
193
    MemoryBound,
194
195
    /// Compute-bound operations (dot, max, min, argmax, argmin)
196
    ///
197
    /// These operations perform significant computation per memory access (arithmetic intensity > 1 op/byte).
198
    /// AVX-512 excels due to wider SIMD parallelism.
199
    ///
200
    /// AVX-512 performance: 7-14x scalar (validated)
201
    /// AVX2 performance: 4-12x scalar (validated)
202
    ComputeBound,
203
204
    /// Mixed operations (fma, sqrt, exp, sigmoid, activations)
205
    ///
206
    /// Performance depends on data size and hardware.
207
    /// Use size-based heuristics or default to AVX2 for safety.
208
    Mixed,
209
}
210
211
/// Detect best SIMD backend for x86/x86_64 platforms
212
///
213
/// **IMPORTANT**: Prefers AVX2 over AVX-512 by default based on performance analysis.
214
///
215
/// AVX-512 is **NOT** universally faster - it causes 10-33% slowdown for memory-bound
216
/// operations (add, mul, sub) due to memory bandwidth bottleneck and thermal throttling.
217
/// See AVX512_ANALYSIS.md for detailed benchmarking results.
218
///
219
/// For operation-specific backend selection, use `select_backend_for_operation()`.
220
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
221
1
fn detect_x86_backend() -> Backend {
222
    // Prefer AVX2 over AVX-512 for safety (AVX-512 causes regressions for memory-bound ops)
223
1
    if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
224
1
        return Backend::AVX2;
225
0
    }
226
    // Note: AVX-512 is intentionally NOT checked here
227
    // Use select_backend_for_operation(OperationType::ComputeBound) for AVX-512
228
0
    if is_x86_feature_detected!("avx") {
229
0
        return Backend::AVX;
230
0
    }
231
0
    if is_x86_feature_detected!("sse2") {
232
0
        return Backend::SSE2;
233
0
    }
234
0
    Backend::Scalar
235
1
}
236
237
/// Detect best SIMD backend for ARM platforms
238
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
239
fn detect_arm_backend() -> Backend {
240
    #[cfg(target_feature = "neon")]
241
    {
242
        Backend::NEON
243
    }
244
    #[cfg(not(target_feature = "neon"))]
245
    {
246
        Backend::Scalar
247
    }
248
}
249
250
/// Detect best SIMD backend for WebAssembly
251
#[cfg(target_arch = "wasm32")]
252
fn detect_wasm_backend() -> Backend {
253
    #[cfg(target_feature = "simd128")]
254
    {
255
        Backend::WasmSIMD
256
    }
257
    #[cfg(not(target_feature = "simd128"))]
258
    {
259
        Backend::Scalar
260
    }
261
}
262
263
/// Select the best available backend for the current platform
264
///
265
/// This function performs runtime CPU feature detection and selects the most
266
/// optimized backend available. The selection follows this priority:
267
///
268
/// **x86/x86_64**:
269
/// 1. AVX-512 (if `avx512f` feature detected)
270
/// 2. AVX2 (if `avx2` and `fma` features detected)
271
/// 3. AVX (if `avx` feature detected)
272
/// 4. SSE2 (baseline for x86_64)
273
/// 5. Scalar (fallback)
274
///
275
/// **ARM**:
276
/// 1. NEON (if available)
277
/// 2. Scalar (fallback)
278
///
279
/// **WASM**: SIMD128 (if available), else Scalar
280
///
281
/// **Other platforms**: Scalar
282
///
283
/// # Returns
284
///
285
/// The most optimized backend available on this CPU/platform
286
///
287
/// # Examples
288
///
289
/// ```
290
/// use trueno::select_best_available_backend;
291
///
292
/// let backend = select_best_available_backend();
293
/// println!("Using backend: {:?}", backend);
294
/// ```
295
257k
pub fn select_best_available_backend() -> Backend {
296
    // Cache backend selection using OnceLock to avoid repeated CPU feature detection
297
    // This eliminates 3-5% overhead from calling is_x86_feature_detected!() repeatedly
298
    static BEST_BACKEND: std::sync::OnceLock<Backend> = std::sync::OnceLock::new();
299
300
257k
    *BEST_BACKEND.get_or_init(|| 
{1
301
        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
302
        {
303
1
            detect_x86_backend()
304
        }
305
306
        #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
307
        {
308
            detect_arm_backend()
309
        }
310
311
        #[cfg(target_arch = "wasm32")]
312
        {
313
            detect_wasm_backend()
314
        }
315
316
        #[cfg(not(any(
317
            target_arch = "x86_64",
318
            target_arch = "x86",
319
            target_arch = "aarch64",
320
            target_arch = "arm",
321
            target_arch = "wasm32"
322
        )))]
323
        {
324
            Backend::Scalar
325
        }
326
1
    })
327
257k
}
328
329
/// Select the optimal backend for a specific operation type
330
///
331
/// This function considers the memory vs compute characteristics of operations
332
/// to select the backend that will provide the best performance. Based on
333
/// comprehensive benchmarking (see AVX512_ANALYSIS.md), AVX-512 is avoided
334
/// for memory-bound operations where it causes 10-33% performance degradation.
335
///
336
/// # Operation Classification
337
///
338
/// - **MemoryBound**: add, sub, mul, div, scale, abs, clamp, lerp, relu
339
///   - Prefer AVX2 (1.0-1.2x scalar) over AVX-512 (0.67-1.20x scalar)
340
///   - Memory bandwidth bottleneck limits wider SIMD benefit
341
///
342
/// - **ComputeBound**: dot, max, min, argmax, argmin, norm_l1, norm_l2, norm_linf
343
///   - Prefer AVX-512 (7-14x scalar) over AVX2 (4-12x scalar)
344
///   - High arithmetic intensity benefits from wider SIMD
345
///
346
/// - **Mixed**: fma, sqrt, exp, ln, sigmoid, tanh, gelu, swish
347
///   - Default to AVX2 for safety (avoids AVX-512 thermal throttling)
348
///   - Size-based heuristics could improve this in future
349
///
350
/// # Backend Selection Priority
351
///
352
/// **For MemoryBound operations**:
353
/// 1. AVX2 (if available) - BEST for memory-bound
354
/// 2. SSE2 (x86_64 baseline)
355
/// 3. AVX-512 (AVOIDED - causes slowdown)
356
/// 4. NEON (ARM)
357
/// 5. WASM SIMD128
358
/// 6. Scalar (fallback)
359
///
360
/// **For ComputeBound operations**:
361
/// 1. AVX-512 (if available) - BEST for compute-bound
362
/// 2. AVX2
363
/// 3. SSE2
364
/// 4. NEON (ARM)
365
/// 5. WASM SIMD128
366
/// 6. Scalar (fallback)
367
///
368
/// # Arguments
369
///
370
/// * `op_type` - The type of operation being performed
371
///
372
/// # Returns
373
///
374
/// The optimal backend for the given operation type
375
///
376
/// # Examples
377
///
378
/// ```
379
/// use trueno::{select_backend_for_operation, OperationType};
380
///
381
/// // Memory-bound operation - prefers AVX2 over AVX-512
382
/// let backend = select_backend_for_operation(OperationType::MemoryBound);
383
///
384
/// // Compute-bound operation - uses AVX-512 if available
385
/// let backend = select_backend_for_operation(OperationType::ComputeBound);
386
/// ```
387
///
388
/// # Performance Impact
389
///
390
/// Using operation-aware backend selection fixes performance regressions:
391
/// - mul with AVX-512: 0.67x → 1.0x (use AVX2 instead)
392
/// - sub with AVX-512: 0.87x → 1.0x (use AVX2 instead)
393
/// - dot with AVX-512: 7.89x (keep AVX-512)
394
0
pub fn select_backend_for_operation(op_type: OperationType) -> Backend {
395
    // Allow unused on non-x86 architectures
396
0
    let _ = &op_type;
397
398
    #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
399
    {
400
0
        select_x86_backend_for_operation(op_type)
401
    }
402
403
    #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
404
    {
405
        detect_arm_backend()
406
    }
407
408
    #[cfg(target_arch = "wasm32")]
409
    {
410
        detect_wasm_backend()
411
    }
412
413
    #[cfg(not(any(
414
        target_arch = "x86_64",
415
        target_arch = "x86",
416
        target_arch = "aarch64",
417
        target_arch = "arm",
418
        target_arch = "wasm32"
419
    )))]
420
    {
421
        Backend::Scalar
422
    }
423
0
}
424
425
/// Select the best x86 backend based on operation type and available features.
426
///
427
/// Separated from `select_backend_for_operation` to reduce cyclomatic complexity.
428
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
429
0
fn select_x86_backend_for_operation(op_type: OperationType) -> Backend {
430
    use std::arch::is_x86_feature_detected;
431
432
    // Check for AVX-512 (only for compute-bound operations)
433
0
    let use_avx512 = op_type == OperationType::ComputeBound && is_x86_feature_detected!("avx512f");
434
0
    if use_avx512 {
435
0
        return Backend::AVX512;
436
0
    }
437
438
    // AVX2 with FMA is preferred for most operations
439
0
    if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
440
0
        return Backend::AVX2;
441
0
    }
442
443
    // Fallback chain: AVX -> SSE2 -> Scalar
444
0
    if is_x86_feature_detected!("avx") {
445
0
        return Backend::AVX;
446
0
    }
447
0
    if is_x86_feature_detected!("sse2") {
448
0
        return Backend::SSE2;
449
0
    }
450
451
0
    Backend::Scalar
452
0
}
453
454
#[cfg(test)]
455
mod tests {
456
    use super::*;
457
458
    #[test]
459
    fn test_backend_enum() {
460
        assert_eq!(Backend::Scalar, Backend::Scalar);
461
        assert_ne!(Backend::Scalar, Backend::AVX2);
462
    }
463
464
    #[test]
465
    fn test_op_complexity_ordering() {
466
        assert!(OpComplexity::Low < OpComplexity::Medium);
467
        assert!(OpComplexity::Medium < OpComplexity::High);
468
    }
469
470
    #[test]
471
    fn test_select_best_available_backend() {
472
        let backend = select_best_available_backend();
473
474
        // On x86_64, we should get at least SSE2 (baseline for x86_64)
475
        // or a more advanced SIMD backend if available
476
        #[cfg(target_arch = "x86_64")]
477
        {
478
            // x86_64 baseline is SSE2, so we should never get Scalar on x86_64
479
            assert_ne!(backend, Backend::Scalar);
480
            // Verify it's one of the x86 SIMD backends
481
            assert!(matches!(
482
                backend,
483
                Backend::SSE2 | Backend::AVX | Backend::AVX2 | Backend::AVX512
484
            ));
485
        }
486
487
        // On other platforms, we might get Scalar or platform-specific SIMD
488
        #[cfg(not(target_arch = "x86_64"))]
489
        {
490
            // Just verify we got a valid backend
491
            assert!(matches!(
492
                backend,
493
                Backend::Scalar
494
                    | Backend::SSE2
495
                    | Backend::AVX
496
                    | Backend::AVX2
497
                    | Backend::AVX512
498
                    | Backend::NEON
499
                    | Backend::WasmSIMD
500
            ));
501
        }
502
    }
503
504
    #[test]
505
    fn test_backend_selection_is_deterministic() {
506
        // Backend selection should be deterministic (same result on multiple calls)
507
        let backend1 = select_best_available_backend();
508
        let backend2 = select_best_available_backend();
509
        assert_eq!(backend1, backend2);
510
    }
511
512
    #[test]
513
    fn test_backend_selection_is_cached() {
514
        // Verify backend selection is cached (OnceLock)
515
        // Multiple calls should return the same backend without re-detection
516
        let backend1 = select_best_available_backend();
517
518
        // Call 1000 times to ensure caching is working
519
        // If not cached, this would be significantly slower
520
        for _ in 0..1000 {
521
            let backend = select_best_available_backend();
522
            assert_eq!(backend, backend1, "Backend selection must be consistent");
523
        }
524
    }
525
526
    #[test]
527
    fn test_backend_select_best() {
528
        // Test Backend::select_best() method
529
        let backend = Backend::select_best();
530
        assert_eq!(backend, select_best_available_backend());
531
    }
532
533
    #[test]
534
    fn test_backend_variants() {
535
        // Test all backend variants
536
        let backends = vec![
537
            Backend::Scalar,
538
            Backend::SSE2,
539
            Backend::AVX,
540
            Backend::AVX2,
541
            Backend::AVX512,
542
            Backend::NEON,
543
            Backend::WasmSIMD,
544
            Backend::GPU,
545
            Backend::Auto,
546
        ];
547
548
        // Verify all variants are distinct
549
        for (i, backend1) in backends.iter().enumerate() {
550
            for (j, backend2) in backends.iter().enumerate() {
551
                if i == j {
552
                    assert_eq!(backend1, backend2);
553
                } else {
554
                    assert_ne!(backend1, backend2);
555
                }
556
            }
557
        }
558
    }
559
560
    #[test]
561
    fn test_backend_debug() {
562
        // Verify Debug trait works
563
        let backend = Backend::AVX2;
564
        let debug_str = format!("{:?}", backend);
565
        assert!(debug_str.contains("AVX2"));
566
567
        let backend = Backend::Auto;
568
        let debug_str = format!("{:?}", backend);
569
        assert!(debug_str.contains("Auto"));
570
    }
571
572
    #[test]
573
    fn test_backend_clone() {
574
        let backend = Backend::AVX2;
575
        #[allow(clippy::clone_on_copy)]
576
        let cloned = backend.clone();
577
        assert_eq!(backend, cloned);
578
    }
579
580
    #[test]
581
    fn test_backend_copy() {
582
        let backend = Backend::SSE2;
583
        let copied = backend;
584
        assert_eq!(backend, copied);
585
    }
586
587
    #[test]
588
    fn test_op_complexity_values() {
589
        assert_eq!(OpComplexity::Low as i32, 0);
590
        assert_eq!(OpComplexity::Medium as i32, 1);
591
        assert_eq!(OpComplexity::High as i32, 2);
592
    }
593
594
    #[test]
595
    fn test_op_complexity_ord() {
596
        // Test PartialOrd
597
        assert!(OpComplexity::Low < OpComplexity::Medium);
598
        assert!(OpComplexity::Medium < OpComplexity::High);
599
        assert!(OpComplexity::Low < OpComplexity::High);
600
601
        // Test Ord
602
        use std::cmp::Ordering;
603
        assert_eq!(OpComplexity::Low.cmp(&OpComplexity::Medium), Ordering::Less);
604
        assert_eq!(
605
            OpComplexity::Medium.cmp(&OpComplexity::High),
606
            Ordering::Less
607
        );
608
        assert_eq!(
609
            OpComplexity::High.cmp(&OpComplexity::Medium),
610
            Ordering::Greater
611
        );
612
        assert_eq!(OpComplexity::Low.cmp(&OpComplexity::Low), Ordering::Equal);
613
    }
614
615
    #[test]
616
    fn test_op_complexity_eq() {
617
        assert_eq!(OpComplexity::Low, OpComplexity::Low);
618
        assert_eq!(OpComplexity::Medium, OpComplexity::Medium);
619
        assert_eq!(OpComplexity::High, OpComplexity::High);
620
        assert_ne!(OpComplexity::Low, OpComplexity::High);
621
    }
622
623
    #[test]
624
    fn test_op_complexity_debug() {
625
        let complexity = OpComplexity::Medium;
626
        let debug_str = format!("{:?}", complexity);
627
        assert!(debug_str.contains("Medium"));
628
    }
629
630
    #[test]
631
    fn test_op_complexity_clone() {
632
        let complexity = OpComplexity::High;
633
        #[allow(clippy::clone_on_copy)]
634
        let cloned = complexity.clone();
635
        assert_eq!(complexity, cloned);
636
    }
637
638
    #[test]
639
    fn test_op_complexity_copy() {
640
        let complexity = OpComplexity::Low;
641
        let copied = complexity;
642
        assert_eq!(complexity, copied);
643
    }
644
645
    #[test]
646
    fn test_trueno_error_reexport() {
647
        // Verify error types are re-exported correctly
648
        let _: Result<()> = Ok(());
649
        let err: Result<()> = Err(TruenoError::EmptyVector);
650
        assert!(err.is_err());
651
    }
652
653
    #[test]
654
    fn test_vector_reexport() {
655
        // Verify Vector is re-exported correctly
656
        let v = Vector::from_slice(&[1.0, 2.0, 3.0]);
657
        assert_eq!(v.len(), 3);
658
    }
659
660
    #[test]
661
    fn test_matrix_reexport() {
662
        // Verify Matrix is re-exported correctly
663
        let m = Matrix::zeros(2, 2);
664
        assert_eq!(m.rows(), 2);
665
        assert_eq!(m.cols(), 2);
666
    }
667
668
    #[cfg(target_arch = "x86_64")]
669
    #[test]
670
    fn test_detect_x86_backend() {
671
        let backend = detect_x86_backend();
672
        // On x86_64, we should get at least SSE2
673
        assert!(matches!(
674
            backend,
675
            Backend::SSE2 | Backend::AVX | Backend::AVX2
676
        ));
677
        // Should NOT return AVX-512 (intentionally avoided for safety)
678
        assert_ne!(backend, Backend::AVX512);
679
    }
680
681
    #[test]
682
    fn test_operation_type_enum() {
683
        // Verify OperationType variants are distinct
684
        assert_ne!(OperationType::MemoryBound, OperationType::ComputeBound);
685
        assert_ne!(OperationType::MemoryBound, OperationType::Mixed);
686
        assert_ne!(OperationType::ComputeBound, OperationType::Mixed);
687
    }
688
689
    #[cfg(target_arch = "x86_64")]
690
    #[test]
691
    fn test_select_backend_for_memory_bound_prefers_avx2() {
692
        let backend = select_backend_for_operation(OperationType::MemoryBound);
693
694
        // Should prefer AVX2 over AVX-512 for memory-bound operations
695
        // (Based on AVX-512 performance analysis showing 0.67-1.01x scalar)
696
        if is_x86_feature_detected!("avx2") {
697
            assert_eq!(backend, Backend::AVX2);
698
        } else if is_x86_feature_detected!("avx") {
699
            assert_eq!(backend, Backend::AVX);
700
        } else if is_x86_feature_detected!("sse2") {
701
            assert_eq!(backend, Backend::SSE2);
702
        } else {
703
            assert_eq!(backend, Backend::Scalar);
704
        }
705
706
        // Critical: Should NEVER return AVX-512 for memory-bound
707
        assert_ne!(backend, Backend::AVX512);
708
    }
709
710
    #[cfg(target_arch = "x86_64")]
711
    #[test]
712
    fn test_select_backend_for_compute_bound_allows_avx512() {
713
        let backend = select_backend_for_operation(OperationType::ComputeBound);
714
715
        // Should prefer AVX-512 for compute-bound operations where it excels (7-14x scalar)
716
        if is_x86_feature_detected!("avx512f") {
717
            assert_eq!(backend, Backend::AVX512);
718
        } else if is_x86_feature_detected!("avx2") {
719
            assert_eq!(backend, Backend::AVX2);
720
        } else if is_x86_feature_detected!("avx") {
721
            assert_eq!(backend, Backend::AVX);
722
        } else if is_x86_feature_detected!("sse2") {
723
            assert_eq!(backend, Backend::SSE2);
724
        } else {
725
            assert_eq!(backend, Backend::Scalar);
726
        }
727
    }
728
729
    #[cfg(target_arch = "x86_64")]
730
    #[test]
731
    fn test_select_backend_for_mixed_prefers_avx2() {
732
        let backend = select_backend_for_operation(OperationType::Mixed);
733
734
        // Mixed operations should default to AVX2 for safety (avoid AVX-512 thermal throttling)
735
        if is_x86_feature_detected!("avx2") {
736
            assert_eq!(backend, Backend::AVX2);
737
        } else if is_x86_feature_detected!("avx") {
738
            assert_eq!(backend, Backend::AVX);
739
        } else if is_x86_feature_detected!("sse2") {
740
            assert_eq!(backend, Backend::SSE2);
741
        } else {
742
            assert_eq!(backend, Backend::Scalar);
743
        }
744
745
        // Should NOT return AVX-512 for mixed operations (safety first)
746
        assert_ne!(backend, Backend::AVX512);
747
    }
748
749
    #[cfg(target_arch = "x86_64")]
750
    #[test]
751
    fn test_default_backend_selection_avoids_avx512() {
752
        // The default backend selection (detect_x86_backend) should avoid AVX-512
753
        let default_backend = select_best_available_backend();
754
755
        // Even on CPUs with AVX-512, default selection should prefer AVX2
756
        if is_x86_feature_detected!("avx2") {
757
            assert_eq!(default_backend, Backend::AVX2);
758
        }
759
760
        // Verify AVX-512 is NOT returned by default
761
        assert_ne!(default_backend, Backend::AVX512);
762
    }
763
764
    #[cfg(target_arch = "x86_64")]
765
    #[test]
766
    fn test_backend_selection_consistency() {
767
        // Memory-bound and Mixed should return same backend (AVX2-first)
768
        let memory_backend = select_backend_for_operation(OperationType::MemoryBound);
769
        let mixed_backend = select_backend_for_operation(OperationType::Mixed);
770
771
        assert_eq!(memory_backend, mixed_backend);
772
773
        // Compute-bound may differ (allows AVX-512)
774
        let compute_backend = select_backend_for_operation(OperationType::ComputeBound);
775
776
        // If AVX-512 is available, compute backend should be different
777
        if is_x86_feature_detected!("avx512f") {
778
            assert_ne!(compute_backend, memory_backend);
779
            assert_eq!(compute_backend, Backend::AVX512);
780
        } else {
781
            // Without AVX-512, all should be the same
782
            assert_eq!(compute_backend, memory_backend);
783
        }
784
    }
785
786
    #[cfg(not(target_arch = "x86_64"))]
787
    #[test]
788
    fn test_select_backend_for_operation_non_x86() {
789
        // On non-x86 platforms, all operation types should return platform-specific backend
790
        let memory = select_backend_for_operation(OperationType::MemoryBound);
791
        let compute = select_backend_for_operation(OperationType::ComputeBound);
792
        let mixed = select_backend_for_operation(OperationType::Mixed);
793
794
        // All should return the same backend (ARM NEON, WASM SIMD, or Scalar)
795
        assert_eq!(memory, compute);
796
        assert_eq!(memory, mixed);
797
798
        #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
799
        {
800
            #[cfg(target_feature = "neon")]
801
            assert_eq!(memory, Backend::NEON);
802
        }
803
804
        #[cfg(target_arch = "wasm32")]
805
        {
806
            #[cfg(target_feature = "simd128")]
807
            assert_eq!(memory, Backend::WasmSIMD);
808
        }
809
    }
810
811
    // ========================================================================
812
    // Additional Coverage Tests
813
    // ========================================================================
814
815
    #[test]
816
    fn test_operation_type_debug() {
817
        let mem = OperationType::MemoryBound;
818
        let debug_str = format!("{:?}", mem);
819
        assert!(debug_str.contains("MemoryBound"));
820
821
        let compute = OperationType::ComputeBound;
822
        let debug_str = format!("{:?}", compute);
823
        assert!(debug_str.contains("ComputeBound"));
824
825
        let mixed = OperationType::Mixed;
826
        let debug_str = format!("{:?}", mixed);
827
        assert!(debug_str.contains("Mixed"));
828
    }
829
830
    #[test]
831
    fn test_operation_type_clone() {
832
        let op_type = OperationType::ComputeBound;
833
        #[allow(clippy::clone_on_copy)]
834
        let cloned = op_type.clone();
835
        assert_eq!(op_type, cloned);
836
    }
837
838
    #[test]
839
    fn test_operation_type_copy() {
840
        let op_type = OperationType::Mixed;
841
        let copied = op_type;
842
        assert_eq!(op_type, copied);
843
    }
844
845
    #[test]
846
    fn test_operation_type_equality() {
847
        assert_eq!(OperationType::MemoryBound, OperationType::MemoryBound);
848
        assert_eq!(OperationType::ComputeBound, OperationType::ComputeBound);
849
        assert_eq!(OperationType::Mixed, OperationType::Mixed);
850
    }
851
852
    #[test]
853
    fn test_backend_all_variants_debug() {
854
        // Test Debug for all backend variants
855
        assert!(format!("{:?}", Backend::Scalar).contains("Scalar"));
856
        assert!(format!("{:?}", Backend::SSE2).contains("SSE2"));
857
        assert!(format!("{:?}", Backend::AVX).contains("AVX"));
858
        assert!(format!("{:?}", Backend::AVX512).contains("AVX512"));
859
        assert!(format!("{:?}", Backend::NEON).contains("NEON"));
860
        assert!(format!("{:?}", Backend::WasmSIMD).contains("WasmSIMD"));
861
        assert!(format!("{:?}", Backend::GPU).contains("GPU"));
862
    }
863
864
    #[test]
865
    fn test_symmetric_eigen_reexport() {
866
        // Verify SymmetricEigen is re-exported correctly
867
        // Just verify it's accessible
868
        let _ = std::mem::size_of::<SymmetricEigen>();
869
    }
870
871
    #[test]
872
    fn test_hash_reexport() {
873
        // Verify hash functions are re-exported correctly
874
        let result = hash_bytes(b"test");
875
        assert_ne!(result, 0);
876
877
        let key_hash = hash_key("test_key");
878
        assert_ne!(key_hash, 0);
879
880
        let keys = ["a", "b", "c", "d"];
881
        let batch_result = hash_keys_batch(&keys);
882
        assert_eq!(batch_result.len(), 4);
883
    }
884
}