- 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
275 lines
9.4 KiB
Rust
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.");
|
|
} |