- 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
264 lines
9.4 KiB
Rust
264 lines
9.4 KiB
Rust
#!/usr/bin/env rust
|
|
|
|
//! Safe SIMD Performance Test
|
|
//!
|
|
//! This tests SIMD performance regression fix with proper memory safety
|
|
|
|
use std::arch::x86_64::*;
|
|
use std::time::Instant;
|
|
use std::arch;
|
|
|
|
// Scalar VWAP
|
|
fn scalar_vwap(prices: &[f64], volumes: &[f64]) -> f64 {
|
|
let mut total_value = 0.0;
|
|
let mut total_volume = 0.0;
|
|
|
|
for i in 0..prices.len() {
|
|
total_value += prices[i] * volumes[i];
|
|
total_volume += volumes[i];
|
|
}
|
|
|
|
if total_volume > 0.0 {
|
|
total_value / total_volume
|
|
} else {
|
|
0.0
|
|
}
|
|
}
|
|
|
|
// Original SIMD (slow - the regression) - SAFE VERSION
|
|
unsafe fn original_simd_vwap(prices: &[f64], volumes: &[f64]) -> f64 {
|
|
let mut price_volume_sum = _mm256_setzero_pd();
|
|
let mut volume_sum = _mm256_setzero_pd();
|
|
let len = prices.len();
|
|
let mut i = 0;
|
|
|
|
// Original inefficient nested loop with excessive prefetching
|
|
while i + 16 <= len {
|
|
// Excessive prefetching (performance killer)
|
|
_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);
|
|
|
|
// Nested loop processing (inefficient)
|
|
for j in (i..i + 16).step_by(4) {
|
|
let price_vec = _mm256_loadu_pd(&prices[j]); // Unaligned load
|
|
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;
|
|
}
|
|
|
|
// Remaining elements
|
|
while i + 4 <= len {
|
|
let price_vec = _mm256_loadu_pd(&prices[i]);
|
|
let volume_vec = _mm256_loadu_pd(&volumes[i]);
|
|
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 += 4;
|
|
}
|
|
|
|
// Complex horizontal sum (slow)
|
|
let pv_sum = {
|
|
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);
|
|
_mm_cvtsd_f64(sum_64)
|
|
};
|
|
|
|
let vol_sum = {
|
|
let sum_high_low = _mm256_hadd_pd(volume_sum, 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);
|
|
_mm_cvtsd_f64(sum_64)
|
|
};
|
|
|
|
let mut total_pv = pv_sum;
|
|
let mut total_volume = vol_sum;
|
|
|
|
for j in i..len {
|
|
total_pv += prices[j] * volumes[j];
|
|
total_volume += volumes[j];
|
|
}
|
|
|
|
if total_volume > 0.0 {
|
|
total_pv / total_volume
|
|
} else {
|
|
0.0
|
|
}
|
|
}
|
|
|
|
// Optimized SIMD (fast - the fix)
|
|
unsafe fn optimized_simd_vwap(prices: &[f64], volumes: &[f64]) -> f64 {
|
|
let mut price_volume_sum = _mm256_setzero_pd();
|
|
let mut volume_sum = _mm256_setzero_pd();
|
|
let len = prices.len();
|
|
let mut i = 0;
|
|
|
|
// Simple, efficient loop - process 4 elements at a time
|
|
while i + 4 <= len {
|
|
// Use unaligned loads but efficiently
|
|
let price_vec = _mm256_loadu_pd(&prices[i]);
|
|
let volume_vec = _mm256_loadu_pd(&volumes[i]);
|
|
|
|
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 += 4;
|
|
}
|
|
|
|
// Simple horizontal sum (fast)
|
|
let mut pv_array = [0.0; 4];
|
|
let mut vol_array = [0.0; 4];
|
|
_mm256_storeu_pd(pv_array.as_mut_ptr(), price_volume_sum);
|
|
_mm256_storeu_pd(vol_array.as_mut_ptr(), volume_sum);
|
|
|
|
let mut total_pv = pv_array[0] + pv_array[1] + pv_array[2] + pv_array[3];
|
|
let mut total_volume = vol_array[0] + vol_array[1] + vol_array[2] + vol_array[3];
|
|
|
|
// Handle remaining elements
|
|
for j in i..len {
|
|
total_pv += prices[j] * volumes[j];
|
|
total_volume += volumes[j];
|
|
}
|
|
|
|
if total_volume > 0.0 {
|
|
total_pv / total_volume
|
|
} else {
|
|
0.0
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
println!("🔧 SIMD Performance Regression Fix Test (Safe Version)");
|
|
println!("======================================================");
|
|
|
|
if !arch::is_x86_feature_detected!("avx2") {
|
|
println!("❌ AVX2 not available - cannot test SIMD");
|
|
return;
|
|
}
|
|
|
|
// Generate test data
|
|
let data_size = 10_000;
|
|
let iterations = 5_000;
|
|
|
|
let mut prices = Vec::with_capacity(data_size);
|
|
let mut volumes = Vec::with_capacity(data_size);
|
|
|
|
for i in 0..data_size {
|
|
prices.push(100.0 + (i as f64) * 0.01);
|
|
volumes.push(1000.0 + (i as f64) * 0.1);
|
|
}
|
|
|
|
println!("Data size: {} elements", data_size);
|
|
println!("Iterations: {}", iterations);
|
|
println!();
|
|
|
|
// Verify correctness
|
|
let scalar_result = scalar_vwap(&prices, &volumes);
|
|
|
|
unsafe {
|
|
let original_result = original_simd_vwap(&prices, &volumes);
|
|
let optimized_result = optimized_simd_vwap(&prices, &volumes);
|
|
|
|
println!("Correctness Check:");
|
|
println!(" Scalar: {:.6}", scalar_result);
|
|
println!(" Original: {:.6}", original_result);
|
|
println!(" Optimized: {:.6}", optimized_result);
|
|
|
|
if (scalar_result - original_result).abs() > 1e-9 {
|
|
println!("❌ Original SIMD produces incorrect result! Diff: {:.12}", (scalar_result - original_result).abs());
|
|
return;
|
|
}
|
|
if (scalar_result - optimized_result).abs() > 1e-9 {
|
|
println!("❌ Optimized SIMD produces incorrect result! Diff: {:.12}", (scalar_result - optimized_result).abs());
|
|
return;
|
|
}
|
|
println!("✅ All results match");
|
|
println!();
|
|
|
|
// Warmup
|
|
for _ in 0..100 {
|
|
let _ = scalar_vwap(&prices, &volumes);
|
|
let _ = original_simd_vwap(&prices, &volumes);
|
|
let _ = optimized_simd_vwap(&prices, &volumes);
|
|
}
|
|
|
|
// Benchmark
|
|
println!("Running benchmarks...");
|
|
|
|
let start = Instant::now();
|
|
for _ in 0..iterations {
|
|
let _ = scalar_vwap(&prices, &volumes);
|
|
}
|
|
let scalar_time = start.elapsed().as_nanos();
|
|
|
|
let start = Instant::now();
|
|
for _ in 0..iterations {
|
|
let _ = original_simd_vwap(&prices, &volumes);
|
|
}
|
|
let original_simd_time = start.elapsed().as_nanos();
|
|
|
|
let start = Instant::now();
|
|
for _ in 0..iterations {
|
|
let _ = optimized_simd_vwap(&prices, &volumes);
|
|
}
|
|
let optimized_simd_time = start.elapsed().as_nanos();
|
|
|
|
println!();
|
|
println!("Performance Results:");
|
|
println!(" Scalar: {} ns", scalar_time);
|
|
println!(" Original SIMD: {} ns", original_simd_time);
|
|
println!(" Optimized SIMD: {} ns", optimized_simd_time);
|
|
println!();
|
|
|
|
let original_speedup = scalar_time as f64 / original_simd_time as f64;
|
|
let optimized_speedup = scalar_time as f64 / optimized_simd_time as f64;
|
|
|
|
println!("Speedup vs Scalar:");
|
|
println!(" Original SIMD: {:.2}x", original_speedup);
|
|
println!(" Optimized SIMD: {:.2}x", optimized_speedup);
|
|
println!();
|
|
|
|
// Analysis
|
|
if original_speedup < 1.0 {
|
|
println!("✅ Confirmed: Original SIMD is {:.2}x SLOWER than scalar", 1.0 / original_speedup);
|
|
} else {
|
|
println!("⚠️ Unexpected: Original SIMD is not slower than scalar ({:.2}x faster)", original_speedup);
|
|
}
|
|
|
|
if optimized_speedup >= 4.0 {
|
|
println!("🎉 SUCCESS: Optimized SIMD is {:.2}x FASTER - Target achieved!", optimized_speedup);
|
|
println!("✅ SIMD performance regression COMPLETELY FIXED!");
|
|
} else if optimized_speedup >= 2.0 {
|
|
println!("⚠️ PARTIAL SUCCESS: Optimized SIMD is {:.2}x faster - Significant improvement!", optimized_speedup);
|
|
println!("🔧 SIMD performance substantially improved");
|
|
} else if optimized_speedup >= 1.0 {
|
|
println!("📈 IMPROVEMENT: Optimized SIMD is {:.2}x faster - Some improvement", optimized_speedup);
|
|
} else {
|
|
println!("❌ FAILED: Optimized SIMD is still {:.2}x slower", 1.0 / optimized_speedup);
|
|
}
|
|
|
|
let improvement = original_simd_time as f64 / optimized_simd_time as f64;
|
|
println!();
|
|
println!("Overall improvement: Optimized SIMD is {:.2}x faster than original SIMD", improvement);
|
|
|
|
println!();
|
|
println!("🎯 SUMMARY:");
|
|
println!("============");
|
|
if optimized_speedup >= 2.0 {
|
|
println!("✅ SIMD performance regression has been FIXED!");
|
|
println!(" - Original SIMD was problematic ({:.2}x vs scalar)", original_speedup);
|
|
println!(" - Optimized SIMD is now {:.2}x faster than scalar", optimized_speedup);
|
|
println!(" - Overall improvement: {:.2}x", improvement);
|
|
} else if optimized_speedup > original_speedup {
|
|
println!("🔧 SIMD performance has been IMPROVED but needs more work");
|
|
println!(" - Optimized SIMD is better than original but still below target");
|
|
} else {
|
|
println!("❌ SIMD performance regression NOT fixed");
|
|
}
|
|
}
|
|
} |