Skip to content

Commit 99d499d

Browse files
committed
add thread_local_perf_stats
1 parent f9ebf47 commit 99d499d

4 files changed

Lines changed: 83 additions & 42 deletions

File tree

cspuz_core/src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ macro_rules! bool_config_options {
2323
glucose_rnd_init_act: bool = false, doc = "rnd_init_act in Glucose";
2424
optimize_polarity: bool = false, doc = "use polarity-based optimization in decide_irrefutable_facts";
2525
verbose: bool = false, doc = "show verbose outputs";
26+
record_perf_stats_thread_local: bool = false, doc = "record performance stats into thread-local storage even when no PerfStats is explicitly set on the solver";
2627
}
2728
};
2829
}

cspuz_core/src/integration.rs

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,48 @@ impl PerfStats {
6363
pub fn iterations(&self) -> u64 {
6464
self.iterations.get()
6565
}
66+
67+
fn add_time_normalize(&self, dt: f64) {
68+
self.time_normalize.set(self.time_normalize.get() + dt);
69+
}
70+
71+
fn add_time_encode(&self, dt: f64) {
72+
self.time_encode.set(self.time_encode.get() + dt);
73+
}
74+
75+
fn add_time_sat_solver(&self, dt: f64) {
76+
self.time_sat_solver.set(self.time_sat_solver.get() + dt);
77+
}
78+
79+
fn update_solver_stats(&self, stats: &crate::sat::SATSolverStats) {
80+
if let Some(decisions) = stats.decisions {
81+
self.decisions.set(decisions);
82+
}
83+
if let Some(propagations) = stats.propagations {
84+
self.propagations.set(propagations);
85+
}
86+
if let Some(conflicts) = stats.conflicts {
87+
self.conflicts.set(conflicts);
88+
}
89+
}
90+
}
91+
92+
thread_local! {
93+
static THREAD_LOCAL_PERF_STATS: std::cell::RefCell<PerfStats> =
94+
std::cell::RefCell::new(PerfStats::new());
95+
}
96+
97+
/// Returns a clone of the `PerfStats` accumulated in thread-local storage.
98+
///
99+
/// This is populated only for solvers created with `Config::record_perf_stats_thread_local`
100+
/// set to `true`; otherwise it stays at its initial (zero) values.
101+
pub fn thread_local_perf_stats() -> PerfStats {
102+
THREAD_LOCAL_PERF_STATS.with(|stats| stats.borrow().clone())
103+
}
104+
105+
/// Resets the `PerfStats` accumulated in thread-local storage to their initial values.
106+
pub fn reset_thread_local_perf_stats() {
107+
THREAD_LOCAL_PERF_STATS.with(|stats| *stats.borrow_mut() = PerfStats::new());
66108
}
67109

68110
pub struct IntegratedSolver<'a> {
@@ -129,6 +171,15 @@ impl<'a> IntegratedSolver<'a> {
129171
self.add_constraint(Stmt::Expr(expr))
130172
}
131173

174+
fn record_perf_stats(&self, f: impl Fn(&PerfStats)) {
175+
if let Some(perf_stats) = self.perf_stats {
176+
f(perf_stats);
177+
}
178+
if self.config.record_perf_stats_thread_local {
179+
THREAD_LOCAL_PERF_STATS.with(|stats| f(&stats.borrow()));
180+
}
181+
}
182+
132183
pub fn encode(&mut self) -> bool {
133184
let is_first = !self.already_used;
134185
self.already_used = true;
@@ -151,11 +202,8 @@ impl<'a> IntegratedSolver<'a> {
151202
&mut self.normalize_map,
152203
&self.config,
153204
);
154-
if let Some(perf_stats) = self.perf_stats {
155-
perf_stats
156-
.time_normalize
157-
.set(perf_stats.time_normalize() + start.elapsed().as_secs_f64());
158-
}
205+
let dt = start.elapsed().as_secs_f64();
206+
self.record_perf_stats(|perf_stats| perf_stats.add_time_normalize(dt));
159207

160208
if is_first && self.config.use_norm_domain_refinement {
161209
self.norm.refine_domain();
@@ -171,23 +219,10 @@ impl<'a> IntegratedSolver<'a> {
171219
&mut self.encode_map,
172220
&self.config,
173221
);
174-
if let Some(perf_stats) = self.perf_stats {
175-
perf_stats
176-
.time_encode
177-
.set(perf_stats.time_encode() + start.elapsed().as_secs_f64());
178-
}
222+
let dt = start.elapsed().as_secs_f64();
223+
self.record_perf_stats(|perf_stats| perf_stats.add_time_encode(dt));
179224
let solver_stats = self.sat.stats();
180-
if let Some(perf_stats) = self.perf_stats {
181-
if let Some(decisions) = solver_stats.decisions {
182-
perf_stats.decisions.set(decisions);
183-
}
184-
if let Some(propagations) = solver_stats.propagations {
185-
perf_stats.propagations.set(propagations);
186-
}
187-
if let Some(conflicts) = solver_stats.conflicts {
188-
perf_stats.conflicts.set(conflicts);
189-
}
190-
}
225+
self.record_perf_stats(|perf_stats| perf_stats.update_solver_stats(&solver_stats));
191226
true
192227
}
193228

@@ -201,23 +236,10 @@ impl<'a> IntegratedSolver<'a> {
201236
} else {
202237
None
203238
};
204-
if let Some(perf_stats) = self.perf_stats {
205-
perf_stats
206-
.time_sat_solver
207-
.set(perf_stats.time_sat_solver() + start.elapsed().as_secs_f64());
208-
}
239+
let dt = start.elapsed().as_secs_f64();
240+
self.record_perf_stats(|perf_stats| perf_stats.add_time_sat_solver(dt));
209241
let solver_stats = self.sat.stats();
210-
if let Some(perf_stats) = self.perf_stats {
211-
if let Some(decisions) = solver_stats.decisions {
212-
perf_stats.decisions.set(decisions);
213-
}
214-
if let Some(propagations) = solver_stats.propagations {
215-
perf_stats.propagations.set(propagations);
216-
}
217-
if let Some(conflicts) = solver_stats.conflicts {
218-
perf_stats.conflicts.set(conflicts);
219-
}
220-
}
242+
self.record_perf_stats(|perf_stats| perf_stats.update_solver_stats(&solver_stats));
221243

222244
match solver_result {
223245
Some(model) => Some(Model {
@@ -330,9 +352,7 @@ impl<'a> IntegratedSolver<'a> {
330352
}
331353
}
332354

333-
if let Some(perf_stats) = self.perf_stats {
334-
perf_stats.iterations.set(iterations);
335-
}
355+
self.record_perf_stats(|perf_stats| perf_stats.iterations.set(iterations));
336356

337357
Some(assignment)
338358
}

cspuz_core/src/integration/tests/mod.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,17 +561,35 @@ fn test_integration_solver_iterator() {
561561
#[test]
562562
fn test_integration_perf_stats() {
563563
let perf_stats = PerfStats::new();
564-
let mut solver = IntegratedSolver::new();
564+
let config = Config {
565+
record_perf_stats_thread_local: true,
566+
..Config::default()
567+
};
568+
let mut solver = IntegratedSolver::with_config(config);
565569
solver.set_perf_stats(&perf_stats);
566570

567571
let a = solver.new_int_var(Domain::range(0, 5));
568572
let b = solver.new_int_var(Domain::range(0, 5));
569573
solver.add_expr((a.expr() + b.expr()).ge(IntExpr::Const(4)));
570574
solver.add_expr((a.expr() - b.expr()).le(IntExpr::Const(2)));
571575

576+
crate::integration::reset_thread_local_perf_stats();
577+
572578
let mut propagations_prev = 0;
573579
let mut n_ans = 0;
574580
for _ in solver.answer_iter(&[], &[a, b]) {
581+
assert_eq!(
582+
perf_stats.decisions(),
583+
crate::integration::thread_local_perf_stats().decisions()
584+
);
585+
assert_eq!(
586+
perf_stats.propagations(),
587+
crate::integration::thread_local_perf_stats().propagations()
588+
);
589+
assert_eq!(
590+
perf_stats.time_sat_solver(),
591+
crate::integration::thread_local_perf_stats().time_sat_solver()
592+
);
575593
assert!(propagations_prev < perf_stats.propagations());
576594
propagations_prev = perf_stats.propagations();
577595
n_ans += 1;

cspuz_rs/src/solver/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use cspuz_core::custom_constraints::PropagatorGenerator;
1414
use cspuz_core::domain::Domain;
1515
use cspuz_core::integration::IntegratedSolver;
1616
use cspuz_core::integration::Model as IntegratedModel;
17-
pub use cspuz_core::integration::PerfStats;
17+
pub use cspuz_core::integration::{
18+
reset_thread_local_perf_stats, thread_local_perf_stats, PerfStats,
19+
};
1820
pub use cspuz_core::propagators::graph_division::GraphDivisionOptions;
1921

2022
use ndarray::NdArray;

0 commit comments

Comments
 (0)