Files
foxhunt/simd_debug.rs
jgrusewski 8cf9437c78 🔧 Partial fixes: S3 integration, SIMD improvements, field access corrections
- Restored S3 storage functionality with AWS SDK
- Fixed field access issues (removed underscore prefixes)
- Created Benzinga historical module
- Initial SIMD optimization (needs consolidation)
- Fixed multiple compilation errors

PENDING: SIMD consolidation, config centralization, shared libraries
2025-09-25 01:05:32 +02:00

275 lines
9.4 KiB
Rust

#!/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<f64>,
}
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<f64>, Vec<f64>) {
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.");
}