Skip to content

Commit 17031c9

Browse files
committed
Incremental Merging
1 parent bfab9a2 commit 17031c9

File tree

6 files changed

+219
-59
lines changed

6 files changed

+219
-59
lines changed

src/eval/cache/incremental.rs

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl IncCache {
7070
idx
7171
}
7272

73-
fn revnode_as_explicit_fun<'a, I>(node: &IncNode, args: I) -> IncNode
73+
fn revnode_as_explicit_fun<'a, I>(node: &mut IncNode, args: I)
7474
where
7575
I: DoubleEndedIterator<Item = &'a Ident>,
7676
{
@@ -85,16 +85,12 @@ impl IncCache {
8585
let as_function =
8686
args.rfold(body, |built, id| RichTerm::from(Term::Fun(*id, built)));
8787

88-
IncNode::new(
89-
Closure {
90-
body: as_function,
91-
env,
92-
},
93-
node.kind,
94-
node.bty.clone(),
95-
)
88+
node.orig = Closure {
89+
body: as_function,
90+
env,
91+
}
9692
}
97-
_ => node.clone(),
93+
_ => (),
9894
}
9995
}
10096

@@ -131,6 +127,27 @@ impl IncCache {
131127
}
132128
}
133129

130+
fn propagate_dirty_vec(&mut self, indices: Vec<CacheIndex>) {
131+
let mut visited = HashSet::new();
132+
let mut stack = indices;
133+
134+
while !stack.is_empty() {
135+
let i = stack.pop().unwrap();
136+
visited.insert(i);
137+
let mut current_node = self.store.get_mut(i).unwrap();
138+
current_node.cached = None;
139+
current_node.state = IncNodeState::Suspended;
140+
println!("IDX: {:?} BLs: {:?}", i, current_node.backlinks);
141+
stack.extend(
142+
current_node
143+
.backlinks
144+
.iter()
145+
.map(|x| x.idx)
146+
.filter(|x| !visited.contains(&x)),
147+
)
148+
}
149+
}
150+
134151
/* Do we need this when we can revert in place?
135152
136153
fn propagate_revert(&mut self, id: Ident, idx: CacheIndex) -> HashMap<Ident, CacheIndex> {
@@ -174,11 +191,30 @@ impl IncCache {
174191
let current_node = self.store.get_mut(*i).unwrap();
175192

176193
for dep in current_node.backlinks.iter_mut() {
177-
dep.idx = *new_indices.get(i).unwrap();
194+
dep.idx = if let Some(idx) = new_indices.get(&dep.idx) {
195+
*idx
196+
} else {
197+
dep.idx
198+
}
178199
}
179200

201+
let mut to_be_updated = vec![];
202+
180203
for dep in current_node.fwdlinks.iter_mut() {
181-
dep.idx = *new_indices.get(i).unwrap();
204+
dep.idx = if let Some(idx) = new_indices.get(&dep.idx) {
205+
*idx
206+
} else {
207+
to_be_updated.push(dep.clone());
208+
dep.idx
209+
}
210+
}
211+
212+
for dep in to_be_updated {
213+
let target_node = self.store.get_mut(dep.idx).unwrap();
214+
target_node.backlinks.push(DependencyLink {
215+
id: dep.id,
216+
idx: *i,
217+
});
182218
}
183219
}
184220

@@ -345,7 +381,7 @@ impl Cache for IncCache {
345381
env: &mut Environment,
346382
fields: I,
347383
) -> RichTerm {
348-
let node = self.store.get(idx).unwrap();
384+
let node = self.store.get_mut(idx).unwrap();
349385

350386
let mut deps_filter: Box<dyn FnMut(&&Ident) -> bool> = match node.bty.clone() {
351387
BindingType::Revertible(FieldDeps::Known(deps)) => {
@@ -355,13 +391,10 @@ impl Cache for IncCache {
355391
BindingType::Normal => Box::new(|_: &&Ident| false),
356392
};
357393

358-
let node_as_function = self.add_node(IncCache::revnode_as_explicit_fun(
359-
node,
360-
fields.clone().filter(&mut deps_filter),
361-
));
394+
IncCache::revnode_as_explicit_fun(node, fields.clone().filter(&mut deps_filter));
362395

363396
let fresh_var = Ident::fresh();
364-
env.insert(fresh_var, node_as_function);
397+
env.insert(fresh_var, idx);
365398

366399
let as_function_closurized = RichTerm::from(Term::Var(fresh_var));
367400
let args = fields.filter_map(|id| deps_filter(&id).then(|| RichTerm::from(Term::Var(*id))));
@@ -370,4 +403,12 @@ impl Cache for IncCache {
370403
RichTerm::from(Term::App(partial_app, arg))
371404
})
372405
}
406+
407+
fn smart_clone(&mut self, v: Vec<CacheIndex>) -> HashMap<CacheIndex, CacheIndex> {
408+
self.smart_clone(v)
409+
}
410+
411+
fn propagate_dirty(&mut self, indices: Vec<CacheIndex>) {
412+
self.propagate_dirty_vec(indices);
413+
}
373414
}

src/eval/cache/lazy.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ use crate::{
44
identifier::Ident,
55
term::{record::FieldDeps, BindingType, RichTerm, Term},
66
};
7-
use std::cell::{Ref, RefCell, RefMut};
87
use std::rc::{Rc, Weak};
8+
use std::{
9+
cell::{Ref, RefCell, RefMut},
10+
collections::HashMap,
11+
};
912

1013
/// The state of a thunk.
1114
///
@@ -355,7 +358,7 @@ impl ThunkData {
355358
/// inside a record may be invalidated by merging, and thus need to store the unaltered original
356359
/// expression. Those aspects are handled and discussed in more detail in
357360
/// [InnerThunkData].
358-
#[derive(Clone, Debug, PartialEq)]
361+
#[derive(Clone, Debug)]
359362
pub struct Thunk {
360363
data: Rc<RefCell<ThunkData>>,
361364
ident_kind: IdentKind,
@@ -560,6 +563,21 @@ impl Thunk {
560563
self.data.borrow().deps()
561564
}
562565
}
566+
567+
impl PartialEq for Thunk {
568+
fn eq(&self, other: &Self) -> bool {
569+
self.data.as_ptr() == other.data.as_ptr() && self.ident_kind == other.ident_kind
570+
}
571+
}
572+
573+
impl Eq for Thunk {}
574+
575+
impl std::hash::Hash for Thunk {
576+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
577+
let raw_ptr = self.data.as_ptr();
578+
(self.ident_kind, raw_ptr).hash(state)
579+
}
580+
}
563581
/// A thunk update frame.
564582
///
565583
/// A thunk update frame is put on the stack whenever a variable is entered, such that once this
@@ -692,4 +710,15 @@ impl Cache for CBNCache {
692710
) -> Result<Self::UpdateIndex, BlackholedError> {
693711
idx.mk_update_frame()
694712
}
713+
714+
fn smart_clone(
715+
&mut self,
716+
v: Vec<CacheIndex>,
717+
) -> std::collections::HashMap<CacheIndex, CacheIndex> {
718+
v.into_iter()
719+
.map(|idx| (idx.clone(), self.revert(&idx)))
720+
.collect()
721+
}
722+
723+
fn propagate_dirty(&mut self, indices: Vec<CacheIndex>) {}
695724
}

src/eval/cache/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
/// The Nickel generic evaluation cache. This module abstracts away the details for managing
24
/// suspended computations and their memoization strategies.
35
///
@@ -94,4 +96,8 @@ pub trait Cache: Clone {
9496
&mut self,
9597
idx: &mut CacheIndex,
9698
) -> Result<Self::UpdateIndex, BlackholedError>;
99+
100+
fn smart_clone(&mut self, v: Vec<CacheIndex>) -> HashMap<CacheIndex, CacheIndex>;
101+
102+
fn propagate_dirty(&mut self, indices: Vec<CacheIndex>);
97103
}

src/eval/fixpoint.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Compute the fixpoint of a recursive record.
2-
use super::{merge::RevertClosurize, *};
3-
use crate::{label::Label, position::TermPos};
2+
use std::collections::HashSet;
3+
4+
use super::{merge::{field_deps, RevertClosurize}, *};
5+
use crate::{position::TermPos, term::record::FieldDeps, label::Label};
46

57
// Update the environment of a term by extending it with a recursive environment. In the general
68
// case, the term is expected to be a variable pointing to the element to be patched. Otherwise, it's
@@ -83,7 +85,7 @@ pub fn rec_env<'a, I: Iterator<Item = (&'a Ident, &'a Field)>, C: Cache>(
8385
// so we start from in the environment of the original record.
8486
let mut final_env = env.clone();
8587
let id_value = Ident::fresh();
86-
final_env.insert(id_value, idx);
88+
final_env.insert(id_value, idx.clone());
8789

8890
let with_ctr_applied = PendingContract::apply_all(
8991
RichTerm::new(Term::Var(id_value), value.pos),
@@ -131,10 +133,15 @@ pub fn rec_env<'a, I: Iterator<Item = (&'a Ident, &'a Field)>, C: Cache>(
131133
env: final_env,
132134
};
133135

134-
Ok((
135-
*id,
136-
cache.add(final_closure, IdentKind::Record, BindingType::Normal),
137-
))
136+
let deps = FieldDeps::from(HashSet::from([*id]));
137+
let mut new_idx = cache.add(
138+
final_closure,
139+
IdentKind::Record,
140+
BindingType::Revertible(deps),
141+
);
142+
cache.build_cached(&mut new_idx, &[(*id, idx)]);
143+
144+
Ok((*id, new_idx))
138145
} else {
139146
let error = EvalError::MissingFieldDef {
140147
id: *id,

0 commit comments

Comments
 (0)