use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use rlx_ir::{Graph, NodeId, Op};
use rlx_runtime::{CompiledGraph, Device, Session};
const MAX_ENTRIES: usize = 512;
thread_local! {
static CACHE: RefCell<HashMap<u64, Rc<RefCell<CompiledGraph>>>> =
RefCell::new(HashMap::new());
static HITS: Cell<u64> = const { Cell::new(0) };
static MISSES: Cell<u64> = const { Cell::new(0) };
}
fn fingerprint(graph: &Graph, outputs: &[NodeId], device: Device) -> u64 {
let mut h = DefaultHasher::new();
device.hash(&mut h);
graph.name.hash(&mut h);
for node in graph.nodes() {
node.id.0.hash(&mut h);
match &node.op {
Op::Constant { data } => {
0u8.hash(&mut h);
data.hash(&mut h);
}
other => format!("{other:?}").hash(&mut h),
}
for inp in &node.inputs {
inp.0.hash(&mut h);
}
format!("{:?}", node.shape).hash(&mut h);
}
for out in outputs {
out.0.hash(&mut h);
}
h.finish()
}
fn lookup(key: u64) -> Option<Rc<RefCell<CompiledGraph>>> {
let hit = CACHE.with(|c| c.borrow().get(&key).cloned());
if hit.is_some() {
HITS.with(|n| n.set(n.get() + 1));
} else {
MISSES.with(|n| n.set(n.get() + 1));
}
hit
}
fn store(key: u64, device: Device, graph: Graph) -> Rc<RefCell<CompiledGraph>> {
let compiled = Rc::new(RefCell::new(Session::new(device).compile(graph)));
CACHE.with(|c| {
let mut map = c.borrow_mut();
if map.len() >= MAX_ENTRIES {
map.clear();
}
map.insert(key, compiled.clone());
});
compiled
}
pub(crate) fn compiled(graph: &Graph, device: Device) -> Rc<RefCell<CompiledGraph>> {
let key = fingerprint(graph, &graph.outputs, device);
lookup(key).unwrap_or_else(|| store(key, device, graph.clone()))
}
pub(crate) fn compiled_output(
graph: &Graph,
output: NodeId,
device: Device,
) -> Rc<RefCell<CompiledGraph>> {
let key = fingerprint(graph, &[output], device);
lookup(key).unwrap_or_else(|| {
let mut g = graph.clone();
g.set_outputs(vec![output]);
store(key, device, g)
})
}
pub fn cache_stats() -> (u64, u64) {
(HITS.with(Cell::get), MISSES.with(Cell::get))
}
pub fn clear_cache() {
CACHE.with(|c| c.borrow_mut().clear());
HITS.with(|n| n.set(0));
MISSES.with(|n| n.set(0));
}