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 {
_placeholder: std::marker::PhantomData<()>,
}
#[cfg(feature = "gpu")]
#[derive(Debug)]
pub struct BindGroupLayout {
_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<()>,
}
#[derive(Debug)]
pub struct PipelineCache {
pipelines: Arc<RwLock<HashMap<ShaderType, Arc<ComputePipeline>>>>,
bind_group_layouts: Arc<RwLock<HashMap<ShaderType, Arc<BindGroupLayout>>>>,
compilation_stats: Arc<RwLock<CompilationStats>>,
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 {
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())),
}
}
pub fn get_or_compile_pipeline(
&self,
shader_type: &ShaderType,
) -> Result<Arc<ComputePipeline>, ComputeError> {
{
let mut stats = self.cache_stats.write().unwrap();
stats.pipeline_requests += 1;
}
{
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));
}
}
let start_time = std::time::Instant::now();
let pipeline = self.compile_pipeline(shader_type)?;
let compilation_time = start_time.elapsed();
{
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;
}
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)
}
pub fn get_or_create_bind_group_layout(
&self,
shader_type: &ShaderType,
) -> Result<Arc<BindGroupLayout>, ComputeError> {
{
let mut stats = self.cache_stats.write().unwrap();
stats.layout_requests += 1;
}
{
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));
}
}
let layout = self.create_bind_group_layout(shader_type)?;
let layout_arc = Arc::new(layout);
{
let mut layouts = self.bind_group_layouts.write().unwrap();
layouts.insert(shader_type.clone(), Arc::clone(&layout_arc));
}
Ok(layout_arc)
}
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(())
}
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)
}
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();
}
}
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
}
fn compile_pipeline(&self, _shader_type: &ShaderType) -> Result<ComputePipeline, ComputeError> {
Ok(ComputePipeline {
_placeholder: std::marker::PhantomData,
})
}
fn create_bind_group_layout(
&self,
_shader_type: &ShaderType,
) -> Result<BindGroupLayout, ComputeError> {
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);
}
}