mirror of
https://github.com/ruvnet/RuView
synced 2026-08-09 20:21:43 +00:00
407b46b206
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/.
351 lines
10 KiB
Rust
351 lines
10 KiB
Rust
//! Highly optimized temporal neural solver
|
|
//! Target: <10µs P99.9 latency
|
|
|
|
use std::arch::x86_64::*;
|
|
use std::alloc::{alloc, dealloc, Layout};
|
|
use std::time::{Duration, Instant};
|
|
|
|
/// SIMD-optimized neural network with pre-allocated memory
|
|
pub struct OptimizedNeuralNetwork {
|
|
// Flattened weight matrices for cache efficiency
|
|
w1_flat: *mut f32, // 32x128 = 4096 elements
|
|
w2_flat: *mut f32, // 4x32 = 128 elements
|
|
b1: [f32; 32],
|
|
b2: [f32; 4],
|
|
|
|
// Pre-allocated buffers
|
|
hidden_buffer: [f32; 32],
|
|
|
|
// Dimensions for safety
|
|
w1_rows: usize,
|
|
w1_cols: usize,
|
|
w2_rows: usize,
|
|
w2_cols: usize,
|
|
}
|
|
|
|
impl OptimizedNeuralNetwork {
|
|
pub fn new() -> Self {
|
|
unsafe {
|
|
// Allocate aligned memory for SIMD
|
|
let w1_layout = Layout::from_size_align(4096 * 4, 32).unwrap();
|
|
let w2_layout = Layout::from_size_align(128 * 4, 32).unwrap();
|
|
|
|
let w1_ptr = alloc(w1_layout) as *mut f32;
|
|
let w2_ptr = alloc(w2_layout) as *mut f32;
|
|
|
|
// Initialize weights
|
|
for i in 0..4096 {
|
|
*w1_ptr.add(i) = ((i as f32) * 0.001).sin() * 0.1;
|
|
}
|
|
for i in 0..128 {
|
|
*w2_ptr.add(i) = ((i as f32) * 0.002).cos() * 0.2;
|
|
}
|
|
|
|
Self {
|
|
w1_flat: w1_ptr,
|
|
w2_flat: w2_ptr,
|
|
b1: [0.0; 32],
|
|
b2: [0.0; 4],
|
|
hidden_buffer: [0.0; 32],
|
|
w1_rows: 32,
|
|
w1_cols: 128,
|
|
w2_rows: 4,
|
|
w2_cols: 32,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub unsafe fn forward_simd(&mut self, input: &[f32; 128]) -> [f32; 4] {
|
|
// Layer 1: Matrix multiplication with AVX2
|
|
for i in 0..self.w1_rows {
|
|
let mut sum = _mm256_setzero_ps();
|
|
|
|
// Process 8 elements at a time with AVX2
|
|
for j in (0..self.w1_cols).step_by(8) {
|
|
let w = _mm256_loadu_ps(self.w1_flat.add(i * self.w1_cols + j));
|
|
let x = _mm256_loadu_ps(input.as_ptr().add(j));
|
|
sum = _mm256_fmadd_ps(w, x, sum);
|
|
}
|
|
|
|
// Sum the 8 floats in the AVX register
|
|
let sum_array = std::mem::transmute::<__m256, [f32; 8]>(sum);
|
|
let mut total = self.b1[i];
|
|
for k in 0..8 {
|
|
total += sum_array[k];
|
|
}
|
|
|
|
// ReLU activation
|
|
self.hidden_buffer[i] = total.max(0.0);
|
|
}
|
|
|
|
// Layer 2: Small matrix, unroll manually
|
|
let mut output = [0.0f32; 4];
|
|
|
|
// Fully unrolled for 4x32
|
|
for i in 0..4 {
|
|
let mut sum = self.b2[i];
|
|
|
|
// Unroll groups of 4
|
|
for j in (0..32).step_by(4) {
|
|
sum += *self.w2_flat.add(i * 32 + j) * self.hidden_buffer[j]
|
|
+ *self.w2_flat.add(i * 32 + j + 1) * self.hidden_buffer[j + 1]
|
|
+ *self.w2_flat.add(i * 32 + j + 2) * self.hidden_buffer[j + 2]
|
|
+ *self.w2_flat.add(i * 32 + j + 3) * self.hidden_buffer[j + 3];
|
|
}
|
|
|
|
output[i] = sum;
|
|
}
|
|
|
|
output
|
|
}
|
|
}
|
|
|
|
impl Drop for OptimizedNeuralNetwork {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
let w1_layout = Layout::from_size_align(4096 * 4, 32).unwrap();
|
|
let w2_layout = Layout::from_size_align(128 * 4, 32).unwrap();
|
|
dealloc(self.w1_flat as *mut u8, w1_layout);
|
|
dealloc(self.w2_flat as *mut u8, w2_layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Optimized Kalman filter with static arrays
|
|
pub struct OptimizedKalmanFilter {
|
|
state: [f64; 8], // 4 positions + 4 velocities
|
|
diagonal_cov: [f64; 8], // Only store diagonal for speed
|
|
process_noise: f64,
|
|
measurement_noise: f64,
|
|
}
|
|
|
|
impl OptimizedKalmanFilter {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
state: [0.0; 8],
|
|
diagonal_cov: [0.1; 8],
|
|
process_noise: 0.001,
|
|
measurement_noise: 0.01,
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn predict_fast(&mut self, dt: f64) -> [f32; 4] {
|
|
// Unrolled position update
|
|
self.state[0] += self.state[4] * dt;
|
|
self.state[1] += self.state[5] * dt;
|
|
self.state[2] += self.state[6] * dt;
|
|
self.state[3] += self.state[7] * dt;
|
|
|
|
// Update covariance diagonal
|
|
for i in 0..8 {
|
|
self.diagonal_cov[i] += self.process_noise;
|
|
}
|
|
|
|
// Return positions as f32
|
|
[
|
|
self.state[0] as f32,
|
|
self.state[1] as f32,
|
|
self.state[2] as f32,
|
|
self.state[3] as f32,
|
|
]
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn update_fast(&mut self, measurement: &[f32; 4]) {
|
|
// Simplified diagonal Kalman update
|
|
for i in 0..4 {
|
|
let error = measurement[i] as f64 - self.state[i];
|
|
let gain = self.diagonal_cov[i] / (self.diagonal_cov[i] + self.measurement_noise);
|
|
self.state[i] += gain * error;
|
|
self.diagonal_cov[i] *= 1.0 - gain;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ultra-fast solver using precomputed LU decomposition
|
|
pub struct OptimizedSolver {
|
|
// Pre-allocated workspace
|
|
workspace: [f64; 16],
|
|
max_iterations: usize,
|
|
}
|
|
|
|
impl OptimizedSolver {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
workspace: [0.0; 16],
|
|
max_iterations: 10, // Reduced iterations for speed
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn solve_fast(&mut self, jacobian: &[[f32; 4]; 4], b: &[f32; 4]) -> (f64, usize) {
|
|
// Initialize with b
|
|
for i in 0..4 {
|
|
self.workspace[i] = b[i] as f64;
|
|
}
|
|
|
|
// Gauss-Seidel iteration (faster convergence than Jacobi)
|
|
let mut residual_norm = 0.0;
|
|
let mut iterations = 0;
|
|
|
|
for iter in 0..self.max_iterations {
|
|
residual_norm = 0.0;
|
|
|
|
// Unrolled Gauss-Seidel update
|
|
for i in 0..4 {
|
|
let mut sum = b[i] as f64;
|
|
|
|
// Use updated values immediately
|
|
for j in 0..4 {
|
|
if i != j {
|
|
sum -= jacobian[i][j] as f64 * self.workspace[j];
|
|
}
|
|
}
|
|
|
|
let diag = jacobian[i][i] as f64;
|
|
if diag.abs() > 1e-10 {
|
|
let new_val = sum / diag;
|
|
let diff = new_val - self.workspace[i];
|
|
residual_norm += diff * diff;
|
|
self.workspace[i] = new_val;
|
|
}
|
|
}
|
|
|
|
iterations = iter + 1;
|
|
|
|
if residual_norm < 1e-12 {
|
|
break;
|
|
}
|
|
}
|
|
|
|
(residual_norm.sqrt(), iterations)
|
|
}
|
|
}
|
|
|
|
/// Complete optimized temporal solver
|
|
pub struct UltraFastTemporalSolver {
|
|
nn: OptimizedNeuralNetwork,
|
|
kalman: OptimizedKalmanFilter,
|
|
solver: OptimizedSolver,
|
|
|
|
// Pre-allocated buffers
|
|
jacobian_buffer: [[f32; 4]; 4],
|
|
prediction_buffer: [f32; 4],
|
|
}
|
|
|
|
impl UltraFastTemporalSolver {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
nn: OptimizedNeuralNetwork::new(),
|
|
kalman: OptimizedKalmanFilter::new(),
|
|
solver: OptimizedSolver::new(),
|
|
jacobian_buffer: [[0.0; 4]; 4],
|
|
prediction_buffer: [0.0; 4],
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn predict_optimized(&mut self, input: &[f32; 128]) -> ([f32; 4], Duration) {
|
|
let start = Instant::now();
|
|
|
|
unsafe {
|
|
// 1. Kalman prediction (optimized)
|
|
let prior = self.kalman.predict_fast(0.001);
|
|
|
|
// 2. Neural network (SIMD optimized)
|
|
let residual = self.nn.forward_simd(input);
|
|
|
|
// 3. Combine (vectorized)
|
|
for i in 0..4 {
|
|
self.prediction_buffer[i] = prior[i] + residual[i];
|
|
}
|
|
|
|
// 4. Simplified Jacobian (identity + small perturbation)
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
self.jacobian_buffer[i][j] = if i == j { 1.0 } else { 0.01 };
|
|
}
|
|
}
|
|
|
|
// 5. Fast solver
|
|
let (_residual, _iters) = self.solver.solve_fast(&self.jacobian_buffer, &self.prediction_buffer);
|
|
|
|
// 6. Fast Kalman update
|
|
self.kalman.update_fast(&self.prediction_buffer);
|
|
}
|
|
|
|
(self.prediction_buffer, start.elapsed())
|
|
}
|
|
}
|
|
|
|
/// Batch processing for even better throughput
|
|
pub struct BatchProcessor {
|
|
solver: UltraFastTemporalSolver,
|
|
}
|
|
|
|
impl BatchProcessor {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
solver: UltraFastTemporalSolver::new(),
|
|
}
|
|
}
|
|
|
|
/// Process multiple inputs with cache-friendly access
|
|
pub fn process_batch(&mut self, inputs: &[[f32; 128]], batch_size: usize) -> Vec<([f32; 4], Duration)> {
|
|
let mut results = Vec::with_capacity(batch_size);
|
|
|
|
// Prefetch next input while processing current
|
|
for i in 0..batch_size.min(inputs.len()) {
|
|
// Prefetch next data
|
|
if i + 1 < inputs.len() {
|
|
unsafe {
|
|
_mm_prefetch(inputs[i + 1].as_ptr() as *const i8, _MM_HINT_T0);
|
|
}
|
|
}
|
|
|
|
let result = self.solver.predict_optimized(&inputs[i]);
|
|
results.push(result);
|
|
}
|
|
|
|
results
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_optimized_performance() {
|
|
let mut solver = UltraFastTemporalSolver::new();
|
|
let input = [0.1f32; 128];
|
|
|
|
// Warmup
|
|
for _ in 0..1000 {
|
|
let _ = solver.predict_optimized(&input);
|
|
}
|
|
|
|
// Benchmark
|
|
let mut timings = Vec::new();
|
|
for _ in 0..1000 {
|
|
let (_pred, duration) = solver.predict_optimized(&input);
|
|
timings.push(duration);
|
|
}
|
|
|
|
timings.sort();
|
|
let p50 = timings[500];
|
|
let p99 = timings[990];
|
|
let p999 = timings[999];
|
|
|
|
println!("Optimized Performance:");
|
|
println!(" P50: {:?}", p50);
|
|
println!(" P99: {:?}", p99);
|
|
println!(" P99.9: {:?}", p999);
|
|
|
|
assert!(p999.as_micros() < 50); // Should be under 50µs
|
|
}
|
|
}
|
|
|
|
// Remove the unnecessary self:: prefix and unused imports
|