diff --git a/Cargo.lock b/Cargo.lock index 043962e..1ecc6f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,6 +163,7 @@ dependencies = [ "bincode", "coremem", "log", + "rayon", "serde", ] diff --git a/crates/applications/buffer_proto5/Cargo.toml b/crates/applications/buffer_proto5/Cargo.toml index e47a76e..07431f4 100644 --- a/crates/applications/buffer_proto5/Cargo.toml +++ b/crates/applications/buffer_proto5/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" bincode = "1.3" # MIT coremem = { path = "../../coremem" } log = "0.4" +rayon = "1.5" # MIT or Apache 2.0 serde = "1.0" diff --git a/crates/applications/buffer_proto5/src/cache.rs b/crates/applications/buffer_proto5/src/cache.rs index a5f64e1..06a52a0 100644 --- a/crates/applications/buffer_proto5/src/cache.rs +++ b/crates/applications/buffer_proto5/src/cache.rs @@ -1,75 +1,215 @@ -use log::trace; use serde::{de::DeserializeOwned, Serialize}; +use std::sync::RwLock; pub struct NoSupplier; -pub struct DiskCache { +pub type DiskCache = DiskCacheImpl, S>; +pub type SyncDiskCache = DiskCacheImpl, S>; + +pub struct DiskCacheImpl { path: String, - entries: Vec<(K, V)>, + entries: E, supplier: S, } -impl DiskCache { +impl DiskCacheImpl +where + E::Key: DeserializeOwned, + E::Value: DeserializeOwned, +{ #[allow(dead_code)] pub fn new(path: &str) -> Self { Self::new_with_supplier(path, NoSupplier) } } -impl DiskCache { +impl DiskCacheImpl +where + E::Key: DeserializeOwned, + E::Value: DeserializeOwned, +{ pub fn new_with_supplier(path: &str, supplier: S) -> Self { let entries = Self::load_from_disk(path).unwrap_or_default(); Self { path: path.into(), - entries, + entries: E::from_vec(entries), supplier, } } - fn load_from_disk(path: &str) -> Option> { + fn load_from_disk(path: &str) -> Option> { let reader = std::io::BufReader::new(std::fs::File::open(path).ok()?); bincode::deserialize_from(reader).ok() } } -impl DiskCache { - pub fn get(&self, k: &K) -> Option<&V> { - self.entries.iter().find(|(comp_k, _v): &&(K, V)| comp_k == k).map(|(_k, v)| v) - } -} - - -impl DiskCache { - pub fn insert(&mut self, k: K, v: V) { - self.entries.push((k, v)); - self.flush(); - } +impl DiskCacheImpl +where + E::Key: Serialize + Clone, + E::Value: Serialize + Clone, +{ fn flush(&self) { let writer = std::io::BufWriter::new(std::fs::File::create(&self.path).unwrap()); - bincode::serialize_into(writer, &self.entries).unwrap(); + bincode::serialize_into(writer, &self.entries.to_vec()).unwrap(); } } -impl DiskCache { +impl DiskCacheImpl +where + E::Key: PartialEq, + E::Value: Clone, +{ + #[allow(dead_code)] + pub fn get(&self, k: &E::Key) -> Option { + self.entries.get(k) + } +} + + +// non-sync insert is mut, while sync is immute +impl DiskCache { + #[allow(dead_code)] + /// insert this k/v ONLY IF NOT PRESENT + pub fn insert(&mut self, k: K, v: V) { + self.entries.insert(k, v); + self.flush(); + } +} +impl SyncDiskCache { + #[allow(dead_code)] + /// insert this k/v ONLY IF NOT PRESENT + pub fn insert(&self, k: K, v: V) { + self.entries.insert(k, v); + self.flush(); + } +} + +// non-sync insert is mut, while sync is immute +impl DiskCache { #[allow(dead_code)] pub fn get_or_insert_with V>(&mut self, k: K, f: F) -> V { - if let Some(v) = self.get(&k) { - return v.clone(); - } - - let v = f(); - self.insert(k, v.clone()); - v + self.entries.get_or_insert_with(k, |_| f()) + } +} +impl SyncDiskCache { + #[allow(dead_code)] + pub fn get_or_insert_with V>(&self, k: K, f: F) -> V { + self.entries.get_or_insert_with(k, |_| f()) } } -impl V> DiskCache { +// non-sync insert is mut, while sync is immute +impl V> DiskCache { + #[allow(dead_code)] pub fn get_or_insert_from_supplier(&mut self, k: K) -> V { + self.entries.get_or_insert_with(k, |k| (self.supplier)(k)) + } +} +impl V> SyncDiskCache { + #[allow(dead_code)] + pub fn get_or_insert_from_supplier(&self, k: K) -> V { + self.entries.get_or_insert_with(k, |k| (self.supplier)(k)) + } +} + +//---------- disk cache entries ---------- +// we have the non-sync and the sync K/V collections, +// which the DiskCacheImpl wraps. + +pub struct Entries(Vec<(K, V)>); +pub struct SyncEntries(RwLock>); + +pub trait EntriesCap { + type Key; + type Value; + + fn from_vec(v: Vec<(Self::Key, Self::Value)>) -> Self; + fn to_vec(&self) -> Vec<(Self::Key, Self::Value)> + where + Self::Key: Clone, + Self::Value: Clone; + fn get(&self, k: &Self::Key) -> Option + where + Self::Key: PartialEq, + Self::Value: Clone; +} + +impl EntriesCap for Entries { + type Key = K; + type Value = V; + fn from_vec(v: Vec<(K, V)>) -> Self { + Self(v) + } + fn to_vec(&self) -> Vec<(K, V)> + where + K: Clone, + V: Clone, + { + self.0.clone() + } + fn get(&self, k: &K) -> Option + where + K: PartialEq, + V: Clone, + { + self.0.iter().find(|(comp_k, _v): &&(K, V)| comp_k == k).map(|(_k, v)| v.clone()) + } +} + +impl EntriesCap for SyncEntries { + type Key = K; + type Value = V; + fn from_vec(v: Vec<(K, V)>) -> Self { + Self(RwLock::new(Entries::from_vec(v))) + } + fn to_vec(&self) -> Vec<(K, V)> + where + K: Clone, + V: Clone, + { + self.0.read().unwrap().to_vec() + } + fn get(&self, k: &K) -> Option + where + K: PartialEq, + V: Clone, + { + self.0.read().unwrap().get(k) + } +} + +impl Entries { + fn insert(&mut self, k: K, v: V) { + + if !self.0.iter().any(|(comp_k, _v): &(K, V)| comp_k == &k) { + self.0.push((k, v)) + } + } +} +impl Entries { + #[allow(dead_code)] + pub fn get_or_insert_with V>(&mut self, k: K, f: F) -> V { if let Some(v) = self.get(&k) { - trace!("get_or_insert_from_supplier hit"); return v.clone(); } - trace!("get_or_insert_from_supplier miss"); - let v = (self.supplier)(&k); + let v = f(&k); + self.insert(k, v.clone()); + v + } +} + +impl SyncEntries { + fn insert(&self, k: K, v: V) { + self.0.write().unwrap().insert(k, v) + } +} +impl SyncEntries { + #[allow(dead_code)] + pub fn get_or_insert_with V>(&self, k: K, f: F) -> V { + if let Some(v) = self.get(&k) { + return v.clone(); + } + + let v = f(&k); self.insert(k, v.clone()); v } diff --git a/crates/applications/buffer_proto5/src/main.rs b/crates/applications/buffer_proto5/src/main.rs index 36ba08c..9ffe251 100644 --- a/crates/applications/buffer_proto5/src/main.rs +++ b/crates/applications/buffer_proto5/src/main.rs @@ -24,10 +24,11 @@ use coremem::sim::spirv::{SpirvSim, WgpuBackend}; use coremem::sim::units::{Seconds, Time as _}; use coremem::stim::{CurlVectorField, Exp, ModulatedVectorField, Sinusoid, TimeVaryingExt as _}; use log::{error, info, warn}; +use rayon::prelude::*; use serde::{Deserialize, Serialize}; mod cache; -use cache::DiskCache; +use cache::SyncDiskCache; type Mat = IsoConductorOr; @@ -673,7 +674,7 @@ fn main() { variants.len() / post_times.len(), ); - let mut geom_cache = DiskCache::new_with_supplier( + let mut geom_cache = SyncDiskCache::new_with_supplier( &format!("{}/.geom_cache", ensure_out_dir(i)), |geom: &GeomParams| derive_geometries(geom.clone()) ); @@ -719,7 +720,7 @@ fn main() { }; let wraps1_choices: Vec<_> = (-120..120) - .into_iter() + .into_par_iter() .filter_map(|wraps1| { let params = GeomParams { wraps1: (wraps1 * 4) as f32,