Skip to content

Commit 8c5a7f6

Browse files
committed
perf(eq): Avoid allocations while collecting activations
1 parent 0a4836c commit 8c5a7f6

File tree

2 files changed

+23
-19
lines changed

2 files changed

+23
-19
lines changed

solver/src/reasoners/eq_alt/constraints.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99

1010
use super::{node::Node, relation::EqRelation};
1111

12-
// TODO: Identical to STN, maybe identify some other common logic and bump up to reasoner module
12+
// TODO: Identical to STN, maybe identify some other common logic (such as identity) and bump up to reasoner module
1313

1414
/// Enabling information for a propagator.
1515
/// A propagator should be enabled iff both literals `active` and `valid` are true.
@@ -37,12 +37,12 @@ impl Enabler {
3737
#[derive(Debug, Clone, Copy)]
3838
pub struct ActivationEvent {
3939
/// the edge to enable
40-
pub prop_id: ConstraintId,
40+
pub constraint_id: ConstraintId,
4141
}
4242

4343
impl ActivationEvent {
44-
pub(crate) fn new(prop_id: ConstraintId) -> Self {
45-
Self { prop_id }
44+
pub fn new(constraint_id: ConstraintId) -> Self {
45+
Self { constraint_id }
4646
}
4747
}
4848

solver/src/reasoners/eq_alt/theory/mod.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use std::{
99
};
1010

1111
use cause::ModelUpdateCause;
12-
use itertools::Itertools;
1312

1413
use crate::{
1514
backtrack::{Backtrack, DecLvl, ObsTrailCursor},
@@ -39,8 +38,8 @@ pub struct AltEqTheory {
3938
enabled_graph: DirectedEqualityGraph,
4039
/// A cursor that lets us track new events since last propagation
4140
model_events: ObsTrailCursor<ModelEvent>,
42-
/// A temporary vec of newly created, unpropagated constraints
43-
new_constraints: VecDeque<ActivationEvent>,
41+
/// A temporary vec of unpropagated constraints
42+
activation_events: VecDeque<ActivationEvent>,
4443
identity: Identity<ModelUpdateCause>,
4544
stats: RefCell<Stats>,
4645
}
@@ -51,7 +50,7 @@ impl AltEqTheory {
5150
constraint_store: Default::default(),
5251
enabled_graph: DirectedEqualityGraph::new(),
5352
model_events: Default::default(),
54-
new_constraints: Default::default(),
53+
activation_events: Default::default(),
5554
identity: Identity::new(ReasonerId::Eq(0)),
5655
stats: Default::default(),
5756
}
@@ -100,7 +99,7 @@ impl AltEqTheory {
10099
if model.entails(prop.enabler.valid) && model.entails(prop.enabler.active) {
101100
// Propagator always active and valid, only need to propagate once
102101
// So don't add watches
103-
self.new_constraints.push_back(ActivationEvent::new(id));
102+
self.activation_events.push_back(ActivationEvent::new(id));
104103
}
105104
}
106105
}
@@ -118,7 +117,7 @@ impl Default for AltEqTheory {
118117

119118
impl Backtrack for AltEqTheory {
120119
fn save_state(&mut self) -> DecLvl {
121-
assert!(self.new_constraints.is_empty());
120+
assert!(self.activation_events.is_empty());
122121
self.enabled_graph.save_state()
123122
}
124123

@@ -127,6 +126,7 @@ impl Backtrack for AltEqTheory {
127126
}
128127

129128
fn restore_last(&mut self) {
129+
self.activation_events.clear();
130130
self.enabled_graph.restore_last();
131131
}
132132
}
@@ -137,11 +137,6 @@ impl Theory for AltEqTheory {
137137
}
138138

139139
fn propagate(&mut self, model: &mut Domains) -> Result<(), Contradiction> {
140-
// Propagate newly created constraints
141-
while let Some(event) = self.new_constraints.pop_front() {
142-
self.propagate_edge(model, event.prop_id)?;
143-
}
144-
145140
while let Some(&event) = self.model_events.pop(model.trail()) {
146141
// Optimisation: If we deactivated an edge with literal l due to a neq cycle, the propagator with literal !l (from reification) is redundant
147142
if let Some(cause) = event.cause.as_external_inference() {
@@ -152,13 +147,22 @@ impl Theory for AltEqTheory {
152147
}
153148

154149
// For each constraint which might be enabled by this event
155-
for (enabler, prop_id) in self.constraint_store.enabled_by(event.new_literal()).collect_vec() {
150+
for (enabler, constraint_id) in self.constraint_store.enabled_by(event.new_literal()) {
156151
// Skip if not enabled
157152
if !model.entails(enabler.active) || !model.entails(enabler.valid) {
158153
continue;
159154
}
160-
self.stats().propagations += 1;
161-
self.propagate_edge(model, prop_id)?;
155+
self.activation_events.push_back(ActivationEvent::new(constraint_id));
156+
}
157+
}
158+
159+
// Propagate all new constraints
160+
while let Some(event) = self.activation_events.pop_front() {
161+
self.stats().propagations += 1;
162+
let prop_res = self.propagate_edge(model, event.constraint_id);
163+
if prop_res.is_err() {
164+
self.activation_events.clear();
165+
return prop_res;
162166
}
163167
}
164168
Ok(())
@@ -229,7 +233,7 @@ mod tests {
229233
F: FnMut(&mut AltEqTheory, &mut Domains) -> T,
230234
{
231235
assert!(
232-
eq.new_constraints.is_empty(),
236+
eq.activation_events.is_empty(),
233237
"Cannot test backtrack when activations pending"
234238
);
235239
eq.save_state();

0 commit comments

Comments
 (0)