Skip to content

Commit ceb9b08

Browse files
authored
perf: Some small perf things (#744)
* Some perf things * Remove unused `QueryOrigin::BaseInput`
1 parent 3ffc9a2 commit ceb9b08

File tree

7 files changed

+63
-83
lines changed

7 files changed

+63
-83
lines changed

src/function/execute.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ where
3737

3838
// If we already executed this query once, then use the tracked-struct ids from the
3939
// previous execution as the starting point for the new one.
40-
if let Some(old_memo) = &opt_old_memo {
40+
if let Some(old_memo) = opt_old_memo {
4141
active_query.seed_tracked_struct_ids(&old_memo.revisions.tracked_struct_ids);
4242
}
4343

@@ -75,7 +75,7 @@ where
7575
// really change, even if some of its inputs have. So we can
7676
// "backdate" its `changed_at` revision to be the same as the
7777
// old value.
78-
if let Some(old_memo) = &opt_old_memo {
78+
if let Some(old_memo) = opt_old_memo {
7979
self.backdate_if_appropriate(old_memo, &mut revisions, &value);
8080
self.diff_outputs(zalsa, db, database_key_index, old_memo, &mut revisions);
8181
}

src/function/fetch.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
accumulator::accumulated_map::InputAccumulatedValues,
55
runtime::StampedValue,
66
zalsa::{Zalsa, ZalsaDatabase},
7-
AsDynDatabase as _, Id,
7+
Id,
88
};
99

1010
impl<C> IngredientImpl<C>
@@ -16,11 +16,14 @@ where
1616
zalsa.unwind_if_revision_cancelled(db);
1717

1818
let memo = self.refresh_memo(db, id);
19+
// SAFETY: We just refreshed the memo so it is guaranteed to contain a value now.
1920
let StampedValue {
2021
value,
2122
durability,
2223
changed_at,
23-
} = memo.revisions.stamped_value(memo.value.as_ref().unwrap());
24+
} = memo
25+
.revisions
26+
.stamped_value(unsafe { memo.value.as_ref().unwrap_unchecked() });
2427

2528
self.lru.record_use(id);
2629

@@ -64,7 +67,7 @@ where
6467
memo_ingredient_index: MemoIngredientIndex,
6568
) -> Option<&'db Memo<C::Output<'db>>> {
6669
let memo_guard = self.get_memo_from_table_for(zalsa, id, memo_ingredient_index);
67-
if let Some(memo) = &memo_guard {
70+
if let Some(memo) = memo_guard {
6871
if memo.value.is_some()
6972
&& self.shallow_verify_memo(db, zalsa, self.database_key_index(id), memo)
7073
{
@@ -83,24 +86,20 @@ where
8386
id: Id,
8487
memo_ingredient_index: MemoIngredientIndex,
8588
) -> Option<&'db Memo<C::Output<'db>>> {
86-
let zalsa_local = db.zalsa_local();
8789
let database_key_index = self.database_key_index(id);
8890

8991
// Try to claim this query: if someone else has claimed it already, go back and start again.
90-
let _claim_guard = zalsa.sync_table_for(id).claim(
91-
db.as_dyn_database(),
92-
zalsa,
93-
zalsa_local,
94-
database_key_index,
95-
memo_ingredient_index,
96-
)?;
92+
let _claim_guard =
93+
zalsa
94+
.sync_table_for(id)
95+
.claim(db, zalsa, database_key_index, memo_ingredient_index)?;
9796

9897
// Push the query on the stack.
99-
let active_query = zalsa_local.push_query(database_key_index);
98+
let active_query = db.zalsa_local().push_query(database_key_index);
10099

101100
// Now that we've claimed the item, check again to see if there's a "hot" value.
102101
let opt_old_memo = self.get_memo_from_table_for(zalsa, id, memo_ingredient_index);
103-
if let Some(old_memo) = &opt_old_memo {
102+
if let Some(old_memo) = opt_old_memo {
104103
if old_memo.value.is_some() && self.deep_verify_memo(db, zalsa, old_memo, &active_query)
105104
{
106105
// Unsafety invariant: memo is present in memo_map and we have verified that it is

src/function/lru.rs

+8
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,26 @@ impl Lru {
1717
}
1818
}
1919

20+
#[inline(always)]
2021
pub(super) fn record_use(&self, index: Id) {
2122
if self.capacity.is_none() {
2223
// LRU is disabled
2324
return;
2425
}
26+
self.insert(index);
27+
}
2528

29+
#[inline(never)]
30+
fn insert(&self, index: Id) {
2631
let mut set = self.set.lock();
2732
set.insert(index);
2833
}
2934

3035
pub(super) fn set_capacity(&mut self, capacity: usize) {
3136
self.capacity = NonZeroUsize::new(capacity);
37+
if self.capacity.is_none() {
38+
self.set.get_mut().clear();
39+
}
3240
}
3341

3442
pub(super) fn for_each_evicted(&mut self, mut cb: impl FnMut(Id)) {

src/function/maybe_changed_after.rs

+14-24
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ where
3030

3131
// Check if we have a verified version: this is the hot path.
3232
let memo_guard = self.get_memo_from_table_for(zalsa, id, memo_ingredient_index);
33-
if let Some(memo) = &memo_guard {
33+
if let Some(memo) = memo_guard {
3434
if self.shallow_verify_memo(db, zalsa, database_key_index, memo) {
3535
return if memo.revisions.changed_at > revision {
3636
MaybeChangedAfter::Yes
@@ -62,15 +62,13 @@ where
6262
) -> Option<MaybeChangedAfter> {
6363
let database_key_index = self.database_key_index(key_index);
6464

65-
let zalsa_local = db.zalsa_local();
6665
let _claim_guard = zalsa.sync_table_for(key_index).claim(
67-
db.as_dyn_database(),
66+
db,
6867
zalsa,
69-
zalsa_local,
7068
database_key_index,
7169
memo_ingredient_index,
7270
)?;
73-
let active_query = zalsa_local.push_query(database_key_index);
71+
let active_query = db.zalsa_local().push_query(database_key_index);
7472

7573
// Load the current memo, if any.
7674
let Some(old_memo) = self.get_memo_from_table_for(zalsa, key_index, memo_ingredient_index)
@@ -187,7 +185,7 @@ where
187185
return true;
188186
}
189187

190-
let inputs = match &old_memo.revisions.origin {
188+
match &old_memo.revisions.origin {
191189
QueryOrigin::Assigned(_) => {
192190
// If the value was assigneed by another query,
193191
// and that query were up-to-date,
@@ -200,15 +198,11 @@ where
200198
// Conditionally specified queries
201199
// where the value is specified
202200
// in rev 1 but not in rev 2.
203-
return false;
204-
}
205-
QueryOrigin::BaseInput => {
206-
// This value was `set` by the mutator thread -- ie, it's a base input and it cannot be out of date.
207-
return true;
201+
false
208202
}
209203
QueryOrigin::DerivedUntracked(_) => {
210204
// Untracked inputs? Have to assume that it changed.
211-
return false;
205+
false
212206
}
213207
QueryOrigin::Derived(edges) => {
214208
// Fully tracked inputs? Iterate over the inputs and check them, one by one.
@@ -219,15 +213,12 @@ where
219213
// it is still up to date is meaningless.
220214
let last_verified_at = old_memo.verified_at.load();
221215
let mut inputs = InputAccumulatedValues::Empty;
216+
let dyn_db = db.as_dyn_database();
222217
for &edge in edges.input_outputs.iter() {
223218
match edge {
224219
QueryEdge::Input(dependency_index) => {
225-
match dependency_index
226-
.maybe_changed_after(db.as_dyn_database(), last_verified_at)
227-
{
228-
MaybeChangedAfter::Yes => {
229-
return false;
230-
}
220+
match dependency_index.maybe_changed_after(dyn_db, last_verified_at) {
221+
MaybeChangedAfter::Yes => return false,
231222
MaybeChangedAfter::No(input_accumulated) => {
232223
inputs |= input_accumulated;
233224
}
@@ -252,17 +243,16 @@ where
252243
// and overwrite the contents.
253244
dependency_index.mark_validated_output(
254245
zalsa,
255-
db.as_dyn_database(),
246+
dyn_db,
256247
database_key_index,
257248
);
258249
}
259250
}
260251
}
261-
inputs
262-
}
263-
};
264252

265-
old_memo.mark_as_verified(db, zalsa.current_revision(), database_key_index, inputs);
266-
true
253+
old_memo.mark_as_verified(db, zalsa.current_revision(), database_key_index, inputs);
254+
true
255+
}
256+
}
267257
}
268258
}

src/function/memo.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ impl<C: Configuration> IngredientImpl<C> {
9090
) {
9191
let map = |memo: &mut Memo<C::Output<'static>>| {
9292
match &memo.revisions.origin {
93-
QueryOrigin::Assigned(_)
94-
| QueryOrigin::DerivedUntracked(_)
95-
| QueryOrigin::BaseInput => {
93+
QueryOrigin::Assigned(_) | QueryOrigin::DerivedUntracked(_) => {
9694
// Careful: Cannot evict memos whose values were
9795
// assigned as output of another query
9896
// or those with untracked inputs

src/table/sync.rs

+24-36
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
use std::{
2-
sync::atomic::{AtomicBool, Ordering},
3-
thread::ThreadId,
4-
};
1+
use std::thread::ThreadId;
52

6-
use parking_lot::RwLock;
3+
use parking_lot::Mutex;
74

85
use crate::{
96
key::DatabaseKeyIndex,
107
runtime::WaitResult,
118
zalsa::{MemoIngredientIndex, Zalsa},
12-
zalsa_local::ZalsaLocal,
139
Database,
1410
};
1511

@@ -19,36 +15,36 @@ use super::util;
1915
/// worker threads.
2016
#[derive(Default)]
2117
pub(crate) struct SyncTable {
22-
syncs: RwLock<Vec<Option<SyncState>>>,
18+
syncs: Mutex<Vec<Option<SyncState>>>,
2319
}
2420

2521
struct SyncState {
2622
id: ThreadId,
2723

2824
/// Set to true if any other queries are blocked,
2925
/// waiting for this query to complete.
30-
anyone_waiting: AtomicBool,
26+
anyone_waiting: bool,
3127
}
3228

3329
impl SyncTable {
30+
#[inline]
3431
pub(crate) fn claim<'me>(
3532
&'me self,
36-
db: &'me dyn Database,
33+
db: &'me (impl ?Sized + Database),
3734
zalsa: &'me Zalsa,
38-
zalsa_local: &ZalsaLocal,
3935
database_key_index: DatabaseKeyIndex,
4036
memo_ingredient_index: MemoIngredientIndex,
4137
) -> Option<ClaimGuard<'me>> {
42-
let mut syncs = self.syncs.write();
38+
let mut syncs = self.syncs.lock();
4339
let thread_id = std::thread::current().id();
4440

4541
util::ensure_vec_len(&mut syncs, memo_ingredient_index.as_usize() + 1);
4642

47-
match &syncs[memo_ingredient_index.as_usize()] {
43+
match &mut syncs[memo_ingredient_index.as_usize()] {
4844
None => {
4945
syncs[memo_ingredient_index.as_usize()] = Some(SyncState {
5046
id: thread_id,
51-
anyone_waiting: AtomicBool::new(false),
47+
anyone_waiting: false,
5248
});
5349
Some(ClaimGuard {
5450
database_key_index,
@@ -61,16 +57,10 @@ impl SyncTable {
6157
id: other_id,
6258
anyone_waiting,
6359
}) => {
64-
// NB: `Ordering::Relaxed` is sufficient here,
65-
// as there are no loads that are "gated" on this
66-
// value. Everything that is written is also protected
67-
// by a lock that must be acquired. The role of this
68-
// boolean is to decide *whether* to acquire the lock,
69-
// not to gate future atomic reads.
70-
anyone_waiting.store(true, Ordering::Relaxed);
60+
*anyone_waiting = true;
7161
zalsa.runtime().block_on_or_unwind(
72-
db,
73-
zalsa_local,
62+
db.as_dyn_database(),
63+
db.zalsa_local(),
7464
database_key_index,
7565
*other_id,
7666
syncs,
@@ -92,30 +82,28 @@ pub(crate) struct ClaimGuard<'me> {
9282
}
9383

9484
impl ClaimGuard<'_> {
95-
fn remove_from_map_and_unblock_queries(&self, wait_result: WaitResult) {
96-
let mut syncs = self.sync_table.syncs.write();
85+
fn remove_from_map_and_unblock_queries(&self) {
86+
let mut syncs = self.sync_table.syncs.lock();
9787

9888
let SyncState { anyone_waiting, .. } =
9989
syncs[self.memo_ingredient_index.as_usize()].take().unwrap();
10090

101-
// NB: `Ordering::Relaxed` is sufficient here,
102-
// see `store` above for explanation.
103-
if anyone_waiting.load(Ordering::Relaxed) {
104-
self.zalsa
105-
.runtime()
106-
.unblock_queries_blocked_on(self.database_key_index, wait_result)
91+
if anyone_waiting {
92+
self.zalsa.runtime().unblock_queries_blocked_on(
93+
self.database_key_index,
94+
if std::thread::panicking() {
95+
WaitResult::Panicked
96+
} else {
97+
WaitResult::Completed
98+
},
99+
)
107100
}
108101
}
109102
}
110103

111104
impl Drop for ClaimGuard<'_> {
112105
fn drop(&mut self) {
113-
let wait_result = if std::thread::panicking() {
114-
WaitResult::Panicked
115-
} else {
116-
WaitResult::Completed
117-
};
118-
self.remove_from_map_and_unblock_queries(wait_result)
106+
self.remove_from_map_and_unblock_queries()
119107
}
120108
}
121109

src/zalsa_local.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,6 @@ pub enum QueryOrigin {
371371
/// The `DatabaseKeyIndex` is the identity of the assigning query.
372372
Assigned(DatabaseKeyIndex),
373373

374-
/// This value was set as a base input to the computation.
375-
BaseInput,
376-
377374
/// The value was derived by executing a function
378375
/// and we were able to track ALL of that function's inputs.
379376
/// Those inputs are described in [`QueryEdges`].
@@ -391,7 +388,7 @@ impl QueryOrigin {
391388
pub(crate) fn inputs(&self) -> impl DoubleEndedIterator<Item = InputDependencyIndex> + '_ {
392389
let opt_edges = match self {
393390
QueryOrigin::Derived(edges) | QueryOrigin::DerivedUntracked(edges) => Some(edges),
394-
QueryOrigin::Assigned(_) | QueryOrigin::BaseInput => None,
391+
QueryOrigin::Assigned(_) => None,
395392
};
396393
opt_edges.into_iter().flat_map(|edges| edges.inputs())
397394
}
@@ -400,7 +397,7 @@ impl QueryOrigin {
400397
pub(crate) fn outputs(&self) -> impl DoubleEndedIterator<Item = OutputDependencyIndex> + '_ {
401398
let opt_edges = match self {
402399
QueryOrigin::Derived(edges) | QueryOrigin::DerivedUntracked(edges) => Some(edges),
403-
QueryOrigin::Assigned(_) | QueryOrigin::BaseInput => None,
400+
QueryOrigin::Assigned(_) => None,
404401
};
405402
opt_edges.into_iter().flat_map(|edges| edges.outputs())
406403
}

0 commit comments

Comments
 (0)