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:
@@ -469,7 +469,7 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let matched_sheets = Self::collect_matching_styles(&vcomp, stylesheet, &[], &structural, ancestors, &sibling_infos[0..i]);
|
let matched_sheets = Self::collect_matching_styles(&vcomp, stylesheet, &[], &structural, ancestors, &sibling_infos[0..i]);
|
||||||
vcomp.computed_style = ComputedStyle::compute(&vcomp.properties, &matched_sheets);
|
vcomp.computed_style = stylesheet.compute_cached(vcomp.type_name, &vcomp.properties, &matched_sheets);
|
||||||
|
|
||||||
let child_ancestors = Self::build_ancestor_chain(ancestors, &vcomp);
|
let child_ancestors = Self::build_ancestor_chain(ancestors, &vcomp);
|
||||||
let comp_child_refs: Vec<&Element<'a>> = comp.children.iter().collect();
|
let comp_child_refs: Vec<&Element<'a>> = comp.children.iter().collect();
|
||||||
@@ -502,7 +502,7 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let matched_sheets = Self::collect_matching_styles(&vnode, stylesheet, &[], &structural, ancestors, &sibling_infos[0..i]);
|
let matched_sheets = Self::collect_matching_styles(&vnode, stylesheet, &[], &structural, ancestors, &sibling_infos[0..i]);
|
||||||
vnode.computed_style = ComputedStyle::compute(&vnode.properties, &matched_sheets);
|
vnode.computed_style = stylesheet.compute_cached(vnode.type_name, &vnode.properties, &matched_sheets);
|
||||||
|
|
||||||
let child_ancestors = Self::build_ancestor_chain(ancestors, &vnode);
|
let child_ancestors = Self::build_ancestor_chain(ancestors, &vnode);
|
||||||
let child_refs: Vec<&Element<'a>> = el.children.iter().collect();
|
let child_refs: Vec<&Element<'a>> = el.children.iter().collect();
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use super::reactive::ElementId;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -458,10 +462,70 @@ impl StyleRule {
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[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 {
|
pub struct StyleSheet {
|
||||||
rules: Vec<StyleRule>,
|
rules: Vec<StyleRule>,
|
||||||
index: Option<StyleIndex>,
|
index: Option<StyleIndex>,
|
||||||
epoch: u64,
|
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 {
|
impl Default for StyleSheet {
|
||||||
@@ -470,6 +534,7 @@ impl Default for StyleSheet {
|
|||||||
rules: Vec::new(),
|
rules: Vec::new(),
|
||||||
index: None,
|
index: None,
|
||||||
epoch: 0,
|
epoch: 0,
|
||||||
|
cache: Mutex::new(StyleCache::new(1024)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,6 +553,7 @@ impl StyleSheet {
|
|||||||
|
|
||||||
pub fn build_index(&mut self) {
|
pub fn build_index(&mut self) {
|
||||||
self.epoch += 1;
|
self.epoch += 1;
|
||||||
|
self.cache.lock().unwrap().clear();
|
||||||
let mut index = StyleIndex::new();
|
let mut index = StyleIndex::new();
|
||||||
index.epoch = self.epoch;
|
index.epoch = self.epoch;
|
||||||
|
|
||||||
@@ -749,6 +815,20 @@ impl StyleSheet {
|
|||||||
props
|
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 {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.rules.is_empty()
|
self.rules.is_empty()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user