#!/usr/bin/env cargo +nightly -Zscript //! Debug SIMD performance issues by testing different components use std::arch::is_x86_feature_detected; use std::time::Instant; #[cfg(target_arch = "x86_64")] mod simd_tests { use std::arch::x86_64::*; use std::time::Instant; // Test 1: Pure SIMD arithmetic (no memory operations) pub unsafe fn test_pure_simd_arithmetic(iterations: usize) -> std::time::Duration { let start = Instant::now(); let a = _mm256_set1_pd(1.5); let b = _mm256_set1_pd(2.5); let mut result = _mm256_setzero_pd(); for _ in 0..iterations { result = _mm256_add_pd(result, _mm256_mul_pd(a, b)); } // Prevent optimization from removing the loop let mut sum = [0.0; 4]; _mm256_storeu_pd(sum.as_mut_ptr(), result); let _total: f64 = sum.iter().sum(); start.elapsed() } // Test 2: SIMD with simple memory loads pub unsafe fn test_simd_memory_loads(data: &[f64], iterations: usize) -> std::time::Duration { let start = Instant::now(); let mut sum = _mm256_setzero_pd(); for _ in 0..iterations { let mut i = 0; while i + 4 <= data.len() { let vec = _mm256_loadu_pd(&data[i]); sum = _mm256_add_pd(sum, vec); i += 4; } } // Prevent optimization let mut result = [0.0; 4]; _mm256_storeu_pd(result.as_mut_ptr(), sum); let _total: f64 = result.iter().sum(); start.elapsed() } // Test 3: Complex SIMD with prefetching (similar to foxhunt implementation) pub unsafe fn test_complex_simd_with_prefetch(prices: &[f64], volumes: &[f64], iterations: usize) -> std::time::Duration { let start = Instant::now(); for _ in 0..iterations { let mut price_volume_sum = _mm256_setzero_pd(); let mut volume_sum = _mm256_setzero_pd(); let len = prices.len(); let mut i = 0; // Process 4 elements at a time with prefetching while i + 16 <= len { // Prefetch next cache lines (like foxhunt code) _mm_prefetch( prices.as_ptr().add(i + 16) as *const i8, _MM_HINT_T0, ); _mm_prefetch( volumes.as_ptr().add(i + 16) as *const i8, _MM_HINT_T0, ); // Process in groups of 4 for j in (i..i + 16).step_by(4) { let price_vec = _mm256_loadu_pd(&prices[j]); let volume_vec = _mm256_loadu_pd(&volumes[j]); let pv_vec = _mm256_mul_pd(price_vec, volume_vec); price_volume_sum = _mm256_add_pd(price_volume_sum, pv_vec); volume_sum = _mm256_add_pd(volume_sum, volume_vec); } i += 16; } // Extract results (like foxhunt code) let sum_high_low = _mm256_hadd_pd(price_volume_sum, price_volume_sum); let sum_128 = _mm256_extractf128_pd(sum_high_low, 1); let sum_64 = _mm_add_pd(_mm256_castpd256_pd128(sum_high_low), sum_128); let _pv_sum = _mm_cvtsd_f64(sum_64); let vol_sum_high_low = _mm256_hadd_pd(volume_sum, volume_sum); let vol_sum_128 = _mm256_extractf128_pd(vol_sum_high_low, 1); let vol_sum_64 = _mm_add_pd(_mm256_castpd256_pd128(vol_sum_high_low), vol_sum_128); let _vol_sum = _mm_cvtsd_f64(vol_sum_64); } start.elapsed() } // Test 4: Aligned memory allocation (like AlignedPrices) pub unsafe fn test_aligned_allocation(size: usize, iterations: usize) -> std::time::Duration { #[repr(align(32))] struct AlignedData { data: Vec, } let start = Instant::now(); for _ in 0..iterations { let mut aligned = AlignedData { data: Vec::with_capacity(size), }; aligned.data.resize(size, 1.0); // Verify alignment let _is_aligned = (aligned.data.as_ptr() as usize) % 32 == 0; // Simple SIMD operation on aligned data let mut sum = _mm256_setzero_pd(); let mut i = 0; while i + 4 <= aligned.data.len() { let vec = _mm256_loadu_pd(&aligned.data[i]); sum = _mm256_add_pd(sum, vec); i += 4; } // Prevent optimization let mut result = [0.0; 4]; _mm256_storeu_pd(result.as_mut_ptr(), sum); let _total: f64 = result.iter().sum(); } start.elapsed() } } fn scalar_baseline(data: &[f64], iterations: usize) -> std::time::Duration { let start = Instant::now(); for _ in 0..iterations { let mut sum = 0.0; for &val in data { sum += val; } let _result = sum; } start.elapsed() } fn scalar_vwap(prices: &[f64], volumes: &[f64], iterations: usize) -> std::time::Duration { let start = Instant::now(); for _ in 0..iterations { let mut total_pv = 0.0; let mut total_vol = 0.0; for i in 0..prices.len() { total_pv += prices[i] * volumes[i]; total_vol += volumes[i]; } let _vwap = if total_vol > 0.0 { total_pv / total_vol } else { 0.0 }; } start.elapsed() } fn generate_test_data(size: usize) -> (Vec, Vec) { let mut prices = Vec::with_capacity(size); let mut volumes = Vec::with_capacity(size); for i in 0..size { prices.push(100.0 + (i as f64 * 0.01)); volumes.push(1000.0 + (i as f64 * 10.0)); } (prices, volumes) } fn main() { println!("🔬 SIMD Performance Debug Analysis"); println!("================================="); if !is_x86_feature_detected!("avx2") { println!("❌ AVX2 not available"); return; } println!("✅ AVX2 detected\n"); let size = 10000; let iterations = 1000; let (prices, volumes) = generate_test_data(size); println!("Testing with {} elements, {} iterations\n", size, iterations); // Test 1: Pure SIMD arithmetic #[cfg(target_arch = "x86_64")] unsafe { let simd_time = simd_tests::test_pure_simd_arithmetic(iterations * 100); println!("📊 Test 1 - Pure SIMD Arithmetic:"); println!(" Time: {:?}", simd_time); println!(" (This should be very fast - pure computation)\n"); } // Test 2: Simple SIMD vs Scalar memory operations let scalar_time = scalar_baseline(&prices, iterations); #[cfg(target_arch = "x86_64")] unsafe { let simd_time = simd_tests::test_simd_memory_loads(&prices, iterations); let speedup = scalar_time.as_nanos() as f64 / simd_time.as_nanos() as f64; println!("📊 Test 2 - Simple Memory Loads:"); println!(" Scalar: {:?}", scalar_time); println!(" SIMD: {:?}", simd_time); println!(" Speedup: {:.2}x", speedup); if speedup < 1.0 { println!(" 🚨 REGRESSION: SIMD {:.2}x slower!", 1.0 / speedup); } else if speedup > 2.0 { println!(" ✅ GOOD: Above 2x speedup"); } else { println!(" ⚠️ SUBOPTIMAL: Below 2x speedup"); } println!(); } // Test 3: Complex SIMD with prefetching (foxhunt style) let scalar_vwap_time = scalar_vwap(&prices, &volumes, iterations); #[cfg(target_arch = "x86_64")] unsafe { let complex_simd_time = simd_tests::test_complex_simd_with_prefetch(&prices, &volumes, iterations); let speedup = scalar_vwap_time.as_nanos() as f64 / complex_simd_time.as_nanos() as f64; println!("📊 Test 3 - Complex SIMD (Foxhunt style):"); println!(" Scalar: {:?}", scalar_vwap_time); println!(" SIMD: {:?}", complex_simd_time); println!(" Speedup: {:.2}x", speedup); if speedup < 1.0 { println!(" 🚨 REGRESSION: SIMD {:.2}x slower!", 1.0 / speedup); println!(" 💡 Issue likely in complex implementation"); } else if speedup > 2.0 { println!(" ✅ GOOD: Above 2x speedup"); } else { println!(" ⚠️ SUBOPTIMAL: Below 2x speedup"); } println!(); } // Test 4: Aligned memory allocation overhead #[cfg(target_arch = "x86_64")] unsafe { let aligned_time = simd_tests::test_aligned_allocation(size, iterations / 10); // Fewer iterations due to allocation cost println!("📊 Test 4 - Aligned Memory Allocation:"); println!(" Time: {:?}", aligned_time); println!(" (High time indicates allocation overhead)\n"); } println!("🔍 Analysis Summary:"); println!("- Test 1 shows pure SIMD computation performance"); println!("- Test 2 shows SIMD vs scalar for simple operations"); println!("- Test 3 replicates the foxhunt SIMD complexity"); println!("- Test 4 shows alignment/allocation overhead"); println!("\nIf Test 2 is good but Test 3 is bad, the issue is in complex implementation."); println!("If Test 4 is very slow, alignment code has problems."); }