Skip to content

Commit f3f6cc4

Browse files
committed
Pool ActiveQuerys in the query stack
1 parent 67b033e commit f3f6cc4

File tree

3 files changed

+86
-24
lines changed

3 files changed

+86
-24
lines changed

src/active_query.rs

+74-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::{mem, ops};
2+
13
use rustc_hash::FxHashMap;
24

35
use super::zalsa_local::{EdgeKind, QueryEdges, QueryOrigin, QueryRevisions};
@@ -56,7 +58,7 @@ pub(crate) struct ActiveQuery {
5658
}
5759

5860
impl ActiveQuery {
59-
pub(super) fn new(database_key_index: DatabaseKeyIndex) -> Self {
61+
pub(crate) fn new(database_key_index: DatabaseKeyIndex) -> Self {
6062
ActiveQuery {
6163
database_key_index,
6264
durability: Durability::MAX,
@@ -70,6 +72,30 @@ impl ActiveQuery {
7072
}
7173
}
7274

75+
fn reset(&mut self, new_database_key_index: DatabaseKeyIndex) {
76+
let Self {
77+
database_key_index,
78+
durability,
79+
changed_at,
80+
input_outputs,
81+
untracked_read,
82+
cycle,
83+
disambiguator_map,
84+
tracked_struct_ids,
85+
accumulated,
86+
} = self;
87+
*database_key_index = new_database_key_index;
88+
*durability = Durability::MAX;
89+
*changed_at = Revision::start();
90+
input_outputs.clear();
91+
*untracked_read = false;
92+
*cycle = None;
93+
disambiguator_map.clear();
94+
// These two are cleared via `mem::take`` when popped off as revisions.
95+
debug_assert!(tracked_struct_ids.is_empty());
96+
_ = accumulated;
97+
}
98+
7399
pub(super) fn add_read(
74100
&mut self,
75101
input: DependencyIndex,
@@ -105,11 +131,11 @@ impl ActiveQuery {
105131
self.input_outputs.contains(&(EdgeKind::Output, key))
106132
}
107133

108-
pub(crate) fn into_revisions(self) -> QueryRevisions {
134+
fn take_revisions(&mut self) -> QueryRevisions {
109135
let input_outputs = if self.input_outputs.is_empty() {
110136
Box::default()
111137
} else {
112-
self.input_outputs.into_iter().collect()
138+
self.input_outputs.iter().copied().collect()
113139
};
114140

115141
let edges = QueryEdges::new(input_outputs);
@@ -124,8 +150,8 @@ impl ActiveQuery {
124150
changed_at: self.changed_at,
125151
origin,
126152
durability: self.durability,
127-
tracked_struct_ids: self.tracked_struct_ids,
128-
accumulated: self.accumulated,
153+
tracked_struct_ids: mem::take(&mut self.tracked_struct_ids),
154+
accumulated: mem::take(&mut self.accumulated),
129155
}
130156
}
131157

@@ -166,3 +192,46 @@ impl ActiveQuery {
166192
result
167193
}
168194
}
195+
196+
#[derive(Debug, Default)]
197+
pub(crate) struct QueryStack {
198+
stack: Vec<ActiveQuery>,
199+
len: usize,
200+
}
201+
202+
impl ops::Deref for QueryStack {
203+
type Target = [ActiveQuery];
204+
205+
fn deref(&self) -> &Self::Target {
206+
&self.stack[..self.len]
207+
}
208+
}
209+
210+
impl ops::DerefMut for QueryStack {
211+
fn deref_mut(&mut self) -> &mut Self::Target {
212+
&mut self.stack[..self.len]
213+
}
214+
}
215+
216+
impl QueryStack {
217+
pub(crate) fn push_new_query(&mut self, database_key_index: DatabaseKeyIndex) {
218+
if self.len < self.stack.len() {
219+
self.stack[self.len].reset(database_key_index);
220+
} else {
221+
self.stack.push(ActiveQuery::new(database_key_index));
222+
}
223+
self.len += 1;
224+
}
225+
226+
pub(crate) fn len(&self) -> usize {
227+
self.len
228+
}
229+
230+
pub(crate) fn pop_into_revisions(&mut self) -> Option<QueryRevisions> {
231+
if self.len == 0 {
232+
return None;
233+
}
234+
self.len -= 1;
235+
Some(self.stack[self.len].take_revisions())
236+
}
237+
}

src/function/execute.rs

-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ where
4646

4747
// Query was not previously executed, or value is potentially
4848
// stale, or value is absent. Let's execute!
49-
let database_key_index = active_query.database_key_index;
5049
let id = database_key_index.key_index;
5150
let value = match Cycle::catch(|| C::execute(db, C::id_to_input(db, id))) {
5251
Ok(v) => v,

src/zalsa_local.rs

+12-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustc_hash::FxHashMap;
22
use tracing::debug;
33

44
use crate::accumulator::accumulated_map::{AccumulatedMap, InputAccumulatedValues};
5-
use crate::active_query::ActiveQuery;
5+
use crate::active_query::QueryStack;
66
use crate::durability::Durability;
77
use crate::key::DatabaseKeyIndex;
88
use crate::key::DependencyIndex;
@@ -36,7 +36,7 @@ pub struct ZalsaLocal {
3636
///
3737
/// Unwinding note: pushes onto this vector must be popped -- even
3838
/// during unwinding.
39-
query_stack: RefCell<Vec<ActiveQuery>>,
39+
query_stack: RefCell<QueryStack>,
4040

4141
/// Stores the most recent page for a given ingredient.
4242
/// This is thread-local to avoid contention.
@@ -46,7 +46,7 @@ pub struct ZalsaLocal {
4646
impl ZalsaLocal {
4747
pub(crate) fn new() -> Self {
4848
ZalsaLocal {
49-
query_stack: RefCell::new(vec![]),
49+
query_stack: RefCell::new(QueryStack::default()),
5050
most_recent_pages: RefCell::new(FxHashMap::default()),
5151
}
5252
}
@@ -87,7 +87,7 @@ impl ZalsaLocal {
8787
#[inline]
8888
pub(crate) fn push_query(&self, database_key_index: DatabaseKeyIndex) -> ActiveQueryGuard<'_> {
8989
let mut query_stack = self.query_stack.borrow_mut();
90-
query_stack.push(ActiveQuery::new(database_key_index));
90+
query_stack.push_new_query(database_key_index);
9191
ActiveQueryGuard {
9292
local_state: self,
9393
database_key_index,
@@ -96,8 +96,8 @@ impl ZalsaLocal {
9696
}
9797

9898
/// Executes a closure within the context of the current active query stacks.
99-
pub(crate) fn with_query_stack<R>(&self, c: impl FnOnce(&mut Vec<ActiveQuery>) -> R) -> R {
100-
c(self.query_stack.borrow_mut().as_mut())
99+
pub(crate) fn with_query_stack<R>(&self, c: impl FnOnce(&mut QueryStack) -> R) -> R {
100+
c(&mut self.query_stack.borrow_mut())
101101
}
102102

103103
fn query_in_progress(&self) -> bool {
@@ -496,15 +496,15 @@ pub(crate) struct ActiveQueryGuard<'me> {
496496
}
497497

498498
impl ActiveQueryGuard<'_> {
499-
fn pop_helper(&self) -> ActiveQuery {
499+
fn pop_impl(&self) -> QueryRevisions {
500500
self.local_state.with_query_stack(|stack| {
501501
// Sanity check: pushes and pops should be balanced.
502502
assert_eq!(stack.len(), self.push_len);
503503
debug_assert_eq!(
504504
stack.last().unwrap().database_key_index,
505505
self.database_key_index
506506
);
507-
stack.pop().unwrap()
507+
stack.pop_into_revisions().unwrap()
508508
})
509509
}
510510

@@ -519,8 +519,8 @@ impl ActiveQueryGuard<'_> {
519519
}
520520

521521
/// Invoked when the query has successfully completed execution.
522-
pub(crate) fn complete(self) -> ActiveQuery {
523-
let query = self.pop_helper();
522+
fn complete(self) -> QueryRevisions {
523+
let query = self.pop_impl();
524524
std::mem::forget(self);
525525
query
526526
}
@@ -530,13 +530,7 @@ impl ActiveQueryGuard<'_> {
530530
/// query's execution.
531531
#[inline]
532532
pub(crate) fn pop(self) -> QueryRevisions {
533-
// Extract accumulated inputs.
534-
let popped_query = self.complete();
535-
536-
// If this frame were a cycle participant, it would have unwound.
537-
assert!(popped_query.cycle.is_none());
538-
539-
popped_query.into_revisions()
533+
self.complete()
540534
}
541535

542536
/// If the active query is registered as a cycle participant, remove and
@@ -549,6 +543,6 @@ impl ActiveQueryGuard<'_> {
549543

550544
impl Drop for ActiveQueryGuard<'_> {
551545
fn drop(&mut self) {
552-
self.pop_helper();
546+
self.pop_impl();
553547
}
554548
}

0 commit comments

Comments
 (0)