rfann 0.1.0

A pure Rust implementation of the Fast Artificial Neural Network (FANN) library
Documentation
//! WebGPU Pipeline Cache and Compilation System
//! Optimized shader pipeline caching for high-performance neural network operations

use crate::webgpu::error::ComputeError;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

#[cfg(feature = "gpu")]
use crate::webgpu::shaders::webgpu_shaders::ShaderType;

#[cfg(not(feature = "gpu"))]
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum ShaderType {
    MatrixVectorMultiply,
    BatchMatrixVectorMultiply,
    ActivationSigmoid,
    ActivationReLU,
    ActivationTanh,
    Other,
}

#[cfg(feature = "gpu")]
#[derive(Debug)]
pub struct ComputePipeline {
    // This would be the actual wgpu::ComputePipeline when WebGPU is available
    _placeholder: std::marker::PhantomData<()>,
}

#[cfg(feature = "gpu")]
#[derive(Debug)]
pub struct BindGroupLayout {
    // This would be the actual wgpu::BindGroupLayout when WebGPU is available
    _placeholder: std::marker::PhantomData<()>,
}

#[cfg(not(feature = "gpu"))]
#[derive(Debug)]
pub struct ComputePipeline {
    _placeholder: std::marker::PhantomData<()>,
}

#[cfg(not(feature = "gpu"))]
#[derive(Debug)]
pub struct BindGroupLayout {
    _placeholder: std::marker::PhantomData<()>,
}

/// High-performance pipeline cache with optimized compilation and reuse
#[derive(Debug)]
pub struct PipelineCache {
    /// Compiled compute pipelines indexed by shader type
    pipelines: Arc<RwLock<HashMap<ShaderType, Arc<ComputePipeline>>>>,

    /// Cached bind group layouts to avoid recompilation
    bind_group_layouts: Arc<RwLock<HashMap<ShaderType, Arc<BindGroupLayout>>>>,

    /// Compilation statistics for performance monitoring
    compilation_stats: Arc<RwLock<CompilationStats>>,

    /// Cache hit/miss statistics
    cache_stats: Arc<RwLock<CacheStats>>,
}

#[derive(Debug, Default, Clone)]
pub struct CompilationStats {
    pub total_compilations: u64,
    pub compilation_time_ns: u64,
    pub cache_hits: u64,
    pub cache_misses: u64,
    pub average_compilation_time_ns: u64,
}

#[derive(Debug, Default, Clone)]
pub struct CacheStats {
    pub pipeline_requests: u64,
    pub pipeline_hits: u64,
    pub layout_requests: u64,
    pub layout_hits: u64,
    pub memory_usage_bytes: u64,
}

impl PipelineCache {
    /// Create a new pipeline cache with optimized settings
    pub fn new() -> Self {
        Self {
            pipelines: Arc::new(RwLock::new(HashMap::new())),
            bind_group_layouts: Arc::new(RwLock::new(HashMap::new())),
            compilation_stats: Arc::new(RwLock::new(CompilationStats::default())),
            cache_stats: Arc::new(RwLock::new(CacheStats::default())),
        }
    }

    /// Get or compile a compute pipeline with automatic caching
    pub fn get_or_compile_pipeline(
        &self,
        shader_type: &ShaderType,
    ) -> Result<Arc<ComputePipeline>, ComputeError> {
        // Update cache statistics
        {
            let mut stats = self.cache_stats.write().unwrap();
            stats.pipeline_requests += 1;
        }

        // Try to get from cache first
        {
            let pipelines = self.pipelines.read().unwrap();
            if let Some(pipeline) = pipelines.get(shader_type) {
                let mut stats = self.cache_stats.write().unwrap();
                stats.pipeline_hits += 1;
                return Ok(Arc::clone(pipeline));
            }
        }

        // Cache miss - compile new pipeline
        let start_time = std::time::Instant::now();
        let pipeline = self.compile_pipeline(shader_type)?;
        let compilation_time = start_time.elapsed();

        // Update compilation statistics
        {
            let mut stats = self.compilation_stats.write().unwrap();
            stats.total_compilations += 1;
            stats.compilation_time_ns += compilation_time.as_nanos() as u64;
            stats.cache_misses += 1;
            stats.average_compilation_time_ns =
                stats.compilation_time_ns / stats.total_compilations;
        }

        // Store in cache
        let pipeline_arc = Arc::new(pipeline);
        {
            let mut pipelines = self.pipelines.write().unwrap();
            pipelines.insert(shader_type.clone(), Arc::clone(&pipeline_arc));
        }

        Ok(pipeline_arc)
    }

    /// Get or create a bind group layout with caching
    pub fn get_or_create_bind_group_layout(
        &self,
        shader_type: &ShaderType,
    ) -> Result<Arc<BindGroupLayout>, ComputeError> {
        // Update cache statistics
        {
            let mut stats = self.cache_stats.write().unwrap();
            stats.layout_requests += 1;
        }

        // Try to get from cache first
        {
            let layouts = self.bind_group_layouts.read().unwrap();
            if let Some(layout) = layouts.get(shader_type) {
                let mut stats = self.cache_stats.write().unwrap();
                stats.layout_hits += 1;
                return Ok(Arc::clone(layout));
            }
        }

        // Cache miss - create new layout
        let layout = self.create_bind_group_layout(shader_type)?;
        let layout_arc = Arc::new(layout);

        // Store in cache
        {
            let mut layouts = self.bind_group_layouts.write().unwrap();
            layouts.insert(shader_type.clone(), Arc::clone(&layout_arc));
        }

        Ok(layout_arc)
    }

    /// Precompile commonly used shaders for optimal startup performance
    pub fn warmup_cache(&self) -> Result<(), ComputeError> {
        let common_shaders = vec![
            ShaderType::MatrixVectorMultiply,
            ShaderType::BatchMatrixVectorMultiply,
            ShaderType::ActivationReLU,
            ShaderType::ActivationSigmoid,
            ShaderType::ActivationTanh,
        ];

        for shader_type in common_shaders {
            self.get_or_compile_pipeline(&shader_type)?;
            self.get_or_create_bind_group_layout(&shader_type)?;
        }

        Ok(())
    }

    /// Get comprehensive cache performance statistics
    pub fn get_performance_stats(&self) -> (CompilationStats, CacheStats) {
        let compilation_stats = {
            let stats = self.compilation_stats.read().unwrap();
            stats.clone()
        };
        let cache_stats = {
            let stats = self.cache_stats.read().unwrap();
            stats.clone()
        };
        (compilation_stats, cache_stats)
    }

    /// Clear cache and reset statistics (useful for testing)
    pub fn clear_cache(&self) {
        {
            let mut pipelines = self.pipelines.write().unwrap();
            pipelines.clear();
        }
        {
            let mut layouts = self.bind_group_layouts.write().unwrap();
            layouts.clear();
        }
        {
            let mut stats = self.compilation_stats.write().unwrap();
            *stats = CompilationStats::default();
        }
        {
            let mut stats = self.cache_stats.write().unwrap();
            *stats = CacheStats::default();
        }
    }

    /// Get cache hit ratio for performance monitoring
    pub fn get_cache_hit_ratio(&self) -> f64 {
        let stats = self.cache_stats.read().unwrap();
        if stats.pipeline_requests == 0 {
            return 0.0;
        }
        stats.pipeline_hits as f64 / stats.pipeline_requests as f64
    }

    /// Compile a shader pipeline (placeholder implementation)
    fn compile_pipeline(&self, _shader_type: &ShaderType) -> Result<ComputePipeline, ComputeError> {
        // In actual implementation, this would:
        // 1. Load shader source based on shader_type
        // 2. Create shader module from WGSL source
        // 3. Create compute pipeline with proper bind group layout
        // 4. Return compiled pipeline

        // For now, return placeholder
        Ok(ComputePipeline {
            _placeholder: std::marker::PhantomData,
        })
    }

    /// Create bind group layout for shader type (placeholder implementation)
    fn create_bind_group_layout(
        &self,
        _shader_type: &ShaderType,
    ) -> Result<BindGroupLayout, ComputeError> {
        // In actual implementation, this would:
        // 1. Define buffer bindings based on shader requirements
        // 2. Create bind group layout with proper visibility and types
        // 3. Return layout for use in pipeline creation and bind groups

        // For now, return placeholder
        Ok(BindGroupLayout {
            _placeholder: std::marker::PhantomData,
        })
    }
}

impl Default for PipelineCache {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pipeline_cache_creation() {
        let cache = PipelineCache::new();
        let (compilation_stats, cache_stats) = cache.get_performance_stats();

        assert_eq!(compilation_stats.total_compilations, 0);
        assert_eq!(cache_stats.pipeline_requests, 0);
    }

    #[test]
    fn test_cache_hit_ratio_calculation() {
        let cache = PipelineCache::new();
        assert_eq!(cache.get_cache_hit_ratio(), 0.0);
    }

    #[test]
    fn test_cache_clearing() {
        let cache = PipelineCache::new();
        cache.clear_cache();
        let (compilation_stats, cache_stats) = cache.get_performance_stats();

        assert_eq!(compilation_stats.total_compilations, 0);
        assert_eq!(cache_stats.pipeline_requests, 0);
    }
}