microcad_lang/render/
cache.rs1use crate::render::{GeometryOutput, HashId};
7
8pub struct RenderCacheItem {
10 content: GeometryOutput,
12 hits: u64,
14 millis: f64,
16 last_access: u64,
18}
19
20impl RenderCacheItem {
21 pub fn new(content: impl Into<GeometryOutput>, millis: f64, last_access: u64) -> Self {
23 Self {
24 content: content.into(),
25 hits: 1,
26 millis,
27 last_access,
28 }
29 }
30
31 pub fn cost(&self, current_time_stamp: u64) -> f64 {
33 let recency = 1.0 / (1.0 + (current_time_stamp - self.last_access) as f64);
39 let frequency = self.hits as f64;
40 let computation_cost = self.millis;
41
42 let weight_recency = 2.3;
44 let weight_frequency = 0.5;
45 let weight_computation = 0.2;
46
47 (weight_recency * recency)
48 + (weight_frequency * frequency)
49 + (weight_computation * computation_cost)
50 }
51}
52
53pub struct RenderCache {
55 current_time_stamp: u64,
57 hits: u64,
59 max_cost: f64,
61 items: rustc_hash::FxHashMap<HashId, RenderCacheItem>,
63}
64
65impl RenderCache {
66 pub fn new() -> Self {
68 Self {
69 current_time_stamp: 0,
70 hits: 0,
71 items: Default::default(),
72 max_cost: std::env::var("MICROCAD_CACHE_MAX_COST")
73 .ok()
74 .and_then(|s| s.parse::<f64>().ok())
75 .unwrap_or(1.2),
76 }
77 }
78
79 pub fn garbage_collection(&mut self) {
81 let old_count = self.items.len();
82 self.items.retain(|hash, item| {
83 let cost = item.cost(self.current_time_stamp);
84 let keep = cost > self.max_cost;
85 log::trace!(
86 "Item {hash:X} cost = {cost}: {keep}",
87 keep = if keep { "🔄" } else { "🗑" }
88 );
89 keep
90 });
91
92 let removed = old_count - self.items.len();
93 log::debug!(
94 "Removed {removed} items from cache. Cache contains {n} items. {hits} cache hits in this cycle.",
95 n = self.items.len(),
96 hits = self.hits,
97 );
98 self.current_time_stamp += 1;
99 self.hits = 0;
100 }
101
102 pub fn clear(&mut self) {
104 self.items.clear();
105 }
106
107 pub fn get(&mut self, hash: &HashId) -> Option<&GeometryOutput> {
109 match self.items.get_mut(hash) {
110 Some(item) => {
111 item.hits += 1;
112 self.hits += 1;
113 item.last_access = self.current_time_stamp;
114 log::trace!(
115 "Cache hit: {hash:X}. Cost: {}",
116 item.cost(self.current_time_stamp)
117 );
118 Some(&item.content)
119 }
120 _ => None,
121 }
122 }
123
124 pub fn insert_with_cost(
126 &mut self,
127 hash: impl Into<HashId>,
128 geo: impl Into<GeometryOutput>,
129 cost: f64,
130 ) -> GeometryOutput {
131 let hash: HashId = hash.into();
132 let geo: GeometryOutput = geo.into();
133 self.items.insert(
134 hash,
135 RenderCacheItem::new(geo.clone(), cost, self.current_time_stamp),
136 );
137 geo
138 }
139}
140
141impl Default for RenderCache {
142 fn default() -> Self {
143 Self::new()
144 }
145}