Phase 8: StyleCache memoization
Add StyleCache struct with hash-keyed LRU-eviction strategy and integrate into StyleSheet via Mutex<StyleCache> so no signature changes are needed on evaluate_vdom_incr. - StyleCache::get_or_compute hashes (type_name, props, epoch) -> ComputedStyle - LRU: full clear on overflow (max 1024) - RefCell avoided in favor of Mutex so StyleSheet remains Sync for parallel matching_rules_batch - Manual Clone impl skips cache (fresh cache on clone) - Call sites in evaluate_vdom_incr switched from ComputedStyle::compute to stylesheet.compute_cached - Cache cleared in build_index after epoch increment (stale entries)
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Mutex;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use super::reactive::ElementId;
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -458,10 +462,70 @@ impl StyleRule {
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StyleCache {
|
||||
entries: HashMap<u64, ComputedStyle>,
|
||||
max_entries: usize,
|
||||
}
|
||||
|
||||
impl StyleCache {
|
||||
pub fn new(max_entries: usize) -> Self {
|
||||
Self { entries: HashMap::new(), max_entries }
|
||||
}
|
||||
|
||||
pub fn get_or_compute(
|
||||
&mut self,
|
||||
type_name: &str,
|
||||
props: &[(Cow<'_, str>, Cow<'_, str>)],
|
||||
epoch: u64,
|
||||
matched_sheets: &[&HashMap<String, String>],
|
||||
) -> ComputedStyle {
|
||||
let key = {
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
type_name.hash(&mut hasher);
|
||||
for (k, v) in props {
|
||||
k.hash(&mut hasher);
|
||||
v.hash(&mut hasher);
|
||||
}
|
||||
epoch.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
};
|
||||
|
||||
if let Some(cached) = self.entries.get(&key) {
|
||||
return cached.clone();
|
||||
}
|
||||
|
||||
let style = ComputedStyle::compute(props, matched_sheets);
|
||||
|
||||
if self.entries.len() >= self.max_entries {
|
||||
self.entries.clear();
|
||||
}
|
||||
self.entries.insert(key, style.clone());
|
||||
|
||||
style
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.entries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyleSheet {
|
||||
rules: Vec<StyleRule>,
|
||||
index: Option<StyleIndex>,
|
||||
epoch: u64,
|
||||
cache: Mutex<StyleCache>,
|
||||
}
|
||||
|
||||
impl Clone for StyleSheet {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
rules: self.rules.clone(),
|
||||
index: self.index.clone(),
|
||||
epoch: self.epoch,
|
||||
cache: Mutex::new(StyleCache::new(1024)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StyleSheet {
|
||||
@@ -470,6 +534,7 @@ impl Default for StyleSheet {
|
||||
rules: Vec::new(),
|
||||
index: None,
|
||||
epoch: 0,
|
||||
cache: Mutex::new(StyleCache::new(1024)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,6 +553,7 @@ impl StyleSheet {
|
||||
|
||||
pub fn build_index(&mut self) {
|
||||
self.epoch += 1;
|
||||
self.cache.lock().unwrap().clear();
|
||||
let mut index = StyleIndex::new();
|
||||
index.epoch = self.epoch;
|
||||
|
||||
@@ -749,6 +815,20 @@ impl StyleSheet {
|
||||
props
|
||||
}
|
||||
|
||||
pub fn compute_cached<'a>(
|
||||
&self,
|
||||
type_name: &str,
|
||||
props: &[(Cow<'_, str>, Cow<'_, str>)],
|
||||
matched_sheets: &[&'a HashMap<String, String>],
|
||||
) -> ComputedStyle {
|
||||
let epoch = self.epoch;
|
||||
self.cache.lock().unwrap().get_or_compute(type_name, props, epoch, matched_sheets)
|
||||
}
|
||||
|
||||
pub fn clear_cache(&self) {
|
||||
self.cache.lock().unwrap().clear();
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.rules.is_empty()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user