Skip to content

Commit ce1baa4

Browse files
committed
Move memo dropping off to another thread
1 parent 4327d6b commit ce1baa4

File tree

18 files changed

+305
-136
lines changed

18 files changed

+305
-136
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ salsa-macros = { version = "0.21.1", path = "components/salsa-macros", optional
1414

1515
boxcar = { version = "0.2.12" }
1616
crossbeam-queue = "0.3.11"
17+
crossbeam-utils = "0.8.21"
1718
dashmap = { version = "6", features = ["raw-api"] }
1819
hashbrown = "0.15"
1920
hashlink = "0.10"

benches/compare.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::mem::transmute;
44
use codspeed_criterion_compat::{
55
criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion,
66
};
7-
use salsa::Setter;
7+
use salsa::{Database, Setter};
88

99
include!("shims/global_alloc_overwrite.rs");
1010

@@ -56,13 +56,16 @@ fn mutating_inputs(c: &mut Criterion) {
5656
group.bench_function(BenchmarkId::new("mutating", n), |b| {
5757
b.iter_batched_ref(
5858
|| {
59-
let db = salsa::DatabaseImpl::default();
59+
let mut db = salsa::DatabaseImpl::default();
6060
let base_string = "hello, world!".to_owned();
6161
let base_len = base_string.len();
6262

6363
let string = base_string.clone().repeat(*n);
6464
let new_len = string.len();
6565

66+
// spawn the LRU thread
67+
db.synthetic_write(salsa::Durability::HIGH);
68+
6669
let input = Input::new(black_box(&db), black_box(base_string.clone()));
6770
let actual_len = length(&db, input);
6871
assert_eq!(black_box(actual_len), base_len);

benches/incremental.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::hint::black_box;
22

33
use codspeed_criterion_compat::{criterion_group, criterion_main, BatchSize, Criterion};
4-
use salsa::Setter;
4+
use salsa::{Database, Setter};
55

66
include!("shims/global_alloc_overwrite.rs");
77

@@ -32,7 +32,9 @@ fn many_tracked_structs(criterion: &mut Criterion) {
3232
criterion.bench_function("many_tracked_structs", |b| {
3333
b.iter_batched_ref(
3434
|| {
35-
let db = salsa::DatabaseImpl::new();
35+
let mut db = salsa::DatabaseImpl::new();
36+
// spawn the LRU thread
37+
db.synthetic_write(salsa::Durability::HIGH);
3638

3739
let input = Input::new(black_box(&db), black_box(1_000));
3840
let input2 = Input::new(black_box(&db), black_box(1));

components/salsa-macro-rules/src/setup_tracked_fn.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ macro_rules! setup_tracked_fn {
249249
zalsa: &$zalsa::Zalsa,
250250
first_index: $zalsa::IngredientIndex,
251251
struct_index: $zalsa::IngredientIndices,
252+
memo_drop_sender: $zalsa::MemoDropSender,
252253
) -> Vec<Box<dyn $zalsa::Ingredient>> {
253254
let struct_index: $zalsa::IngredientIndices = $zalsa::macro_if! {
254255
if $needs_interner {
@@ -282,12 +283,16 @@ macro_rules! setup_tracked_fn {
282283
)
283284
};
284285

285-
let fn_ingredient = <$zalsa::function::IngredientImpl<$Configuration>>::new(
286-
first_index,
287-
memo_ingredient_indices,
288-
$lru,
289-
zalsa.views().downcaster_for::<dyn $Db>(),
290-
);
286+
// SAFETY: We pass the MemoEntryType for this Configuration, and we lookup the memo types table correctly.
287+
let fn_ingredient = unsafe {
288+
<$zalsa::function::IngredientImpl<$Configuration>>::new(
289+
first_index,
290+
memo_ingredient_indices,
291+
$lru,
292+
zalsa.views().downcaster_for::<dyn $Db>(),
293+
memo_drop_sender
294+
)
295+
};
291296
$zalsa::macro_if! {
292297
if $needs_interner {
293298
vec![

src/accumulator.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::cycle::CycleHeads;
1111
use crate::function::VerifyResult;
1212
use crate::ingredient::{Ingredient, Jar};
1313
use crate::loom::sync::Arc;
14-
use crate::plumbing::{IngredientIndices, ZalsaLocal};
14+
use crate::plumbing::{IngredientIndices, MemoDropSender, ZalsaLocal};
1515
use crate::table::memo::MemoTableTypes;
1616
use crate::zalsa::{IngredientIndex, Zalsa};
1717
use crate::{Database, Id, Revision};
@@ -47,6 +47,7 @@ impl<A: Accumulator> Jar for JarImpl<A> {
4747
_zalsa: &Zalsa,
4848
first_index: IngredientIndex,
4949
_dependencies: IngredientIndices,
50+
_: MemoDropSender,
5051
) -> Vec<Box<dyn Ingredient>> {
5152
vec![Box::new(<IngredientImpl<A>>::new(first_index))]
5253
}

src/exclusive.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Bare-bones polyfill for the unstable [`std::sync::Exclusive`] type.
2+
3+
pub struct Exclusive<T: ?Sized> {
4+
inner: T,
5+
}
6+
7+
// SAFETY: We only hand out mutable access to the inner value through a mutable reference to the
8+
// wrapper.
9+
// Therefore we cannot alias the inner value making it trivially sync.
10+
unsafe impl<T> Sync for Exclusive<T> {}
11+
12+
impl<T> Exclusive<T> {
13+
pub fn new(inner: T) -> Self {
14+
Self { inner }
15+
}
16+
17+
pub fn get_mut(&mut self) -> &mut T {
18+
&mut self.inner
19+
}
20+
}

src/function.rs

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use std::any::Any;
22
use std::fmt;
3+
use std::marker::PhantomData;
34
use std::ptr::NonNull;
45

56
pub(crate) use maybe_changed_after::VerifyResult;
67

78
use crate::accumulator::accumulated_map::{AccumulatedMap, InputAccumulatedValues};
89
use crate::cycle::{CycleHeadKind, CycleHeads, CycleRecoveryAction, CycleRecoveryStrategy};
9-
use crate::function::delete::DeletedEntries;
1010
use crate::function::sync::{ClaimResult, SyncTable};
1111
use crate::ingredient::Ingredient;
1212
use crate::key::DatabaseKeyIndex;
1313
use crate::loom::sync::Arc;
14-
use crate::plumbing::MemoIngredientMap;
14+
use crate::plumbing::{MemoDropSender, MemoIngredientMap};
1515
use crate::salsa_struct::SalsaStructInDb;
1616
use crate::table::memo::MemoTableTypes;
1717
use crate::table::Table;
@@ -22,7 +22,6 @@ use crate::{Database, Id, Revision};
2222

2323
mod accumulated;
2424
mod backdate;
25-
mod delete;
2625
mod diff_outputs;
2726
mod execute;
2827
mod fetch;
@@ -137,7 +136,8 @@ pub struct IngredientImpl<C: Configuration> {
137136
/// current revision: you would be right, but we are being defensive, because
138137
/// we don't know that we can trust the database to give us the same runtime
139138
/// everytime and so forth.
140-
deleted_entries: DeletedEntries<C>,
139+
delete: MemoDropSender,
140+
config: PhantomData<fn(C) -> C>,
141141
}
142142

143143
impl<C> IngredientImpl<C>
@@ -149,14 +149,16 @@ where
149149
memo_ingredient_indices: <C::SalsaStruct<'static> as SalsaStructInDb>::MemoIngredientMap,
150150
lru: usize,
151151
view_caster: DatabaseDownCaster<C::DbView>,
152+
delete: MemoDropSender,
152153
) -> Self {
153154
Self {
154155
index,
155156
memo_ingredient_indices,
156157
lru: lru::Lru::new(lru),
157-
deleted_entries: Default::default(),
158158
view_caster,
159159
sync_table: SyncTable::new(index),
160+
delete,
161+
config: PhantomData,
160162
}
161163
}
162164

@@ -196,16 +198,7 @@ where
196198
// FIXME: Use `Box::into_non_null` once stable
197199
let memo = NonNull::from(Box::leak(Box::new(memo)));
198200

199-
if let Some(old_value) =
200-
self.insert_memo_into_table_for(zalsa, id, memo, memo_ingredient_index)
201-
{
202-
// In case there is a reference to the old memo out there, we have to store it
203-
// in the deleted entries. This will get cleared when a new revision starts.
204-
//
205-
// SAFETY: Once the revision starts, there will be no outstanding borrows to the
206-
// memo contents, and so it will be safe to free.
207-
unsafe { self.deleted_entries.push(old_value) };
208-
}
201+
self.insert_memo_into_table_for(zalsa, id, memo, memo_ingredient_index);
209202
// SAFETY: memo has been inserted into the table
210203
unsafe { self.extend_memo_lifetime(memo.as_ref()) }
211204
}
@@ -301,10 +294,9 @@ where
301294
Self::evict_value_from_memo_for(
302295
table.memos_mut(evict),
303296
self.memo_ingredient_indices.get(ingredient_index),
297+
&self.delete,
304298
)
305299
});
306-
307-
self.deleted_entries.clear();
308300
}
309301

310302
fn debug_name(&self) -> &'static str {

src/function/delete.rs

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/function/memo.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::cycle::{CycleHeadKind, CycleHeads, EMPTY_CYCLE_HEADS};
77
use crate::function::{Configuration, IngredientImpl};
88
use crate::key::DatabaseKeyIndex;
99
use crate::loom::sync::atomic::Ordering;
10+
use crate::plumbing::MemoDropSender;
1011
use crate::revision::AtomicRevision;
1112
use crate::table::memo::MemoTableWithTypesMut;
1213
use crate::zalsa::{MemoIngredientIndex, Zalsa};
@@ -16,27 +17,23 @@ use crate::{Event, EventKind, Id, Revision};
1617
impl<C: Configuration> IngredientImpl<C> {
1718
/// Inserts the memo for the given key; (atomically) overwrites and returns any previously existing memo
1819
pub(super) fn insert_memo_into_table_for<'db>(
19-
&self,
20+
&'db self,
2021
zalsa: &'db Zalsa,
2122
id: Id,
2223
memo: NonNull<Memo<C::Output<'db>>>,
2324
memo_ingredient_index: MemoIngredientIndex,
24-
) -> Option<NonNull<Memo<C::Output<'db>>>> {
25+
) {
2526
// SAFETY: The table stores 'static memos (to support `Any`), the memos are in fact valid
2627
// for `'db` though as we delay their dropping to the end of a revision.
2728
let static_memo = unsafe {
2829
transmute::<NonNull<Memo<C::Output<'db>>>, NonNull<Memo<C::Output<'static>>>>(memo)
2930
};
30-
let old_static_memo = zalsa
31+
let old_memo = zalsa
3132
.memo_table_for(id)
32-
.insert(memo_ingredient_index, static_memo)?;
33-
// SAFETY: The table stores 'static memos (to support `Any`), the memos are in fact valid
34-
// for `'db` though as we delay their dropping to the end of a revision.
35-
Some(unsafe {
36-
transmute::<NonNull<Memo<C::Output<'static>>>, NonNull<Memo<C::Output<'db>>>>(
37-
old_static_memo,
38-
)
39-
})
33+
.insert(memo_ingredient_index, static_memo);
34+
if let Some(old_memo) = old_memo {
35+
self.delete.delay(old_memo);
36+
}
4037
}
4138

4239
/// Loads the current memo for `key_index`. This does not hold any sort of
@@ -62,9 +59,11 @@ impl<C: Configuration> IngredientImpl<C> {
6259
pub(super) fn evict_value_from_memo_for(
6360
table: MemoTableWithTypesMut<'_>,
6461
memo_ingredient_index: MemoIngredientIndex,
62+
delayed: &MemoDropSender,
6563
) {
66-
let map = |memo: &mut Memo<C::Output<'static>>| {
67-
match &memo.revisions.origin {
64+
if let Some(memo) = table.fetch_raw::<Memo<C::Output<'static>>>(memo_ingredient_index) {
65+
// SAFETY: The memo is live
66+
match unsafe { &memo.as_ref().revisions.origin } {
6867
QueryOrigin::Assigned(_)
6968
| QueryOrigin::DerivedUntracked(_)
7069
| QueryOrigin::FixpointInitial => {
@@ -73,14 +72,9 @@ impl<C: Configuration> IngredientImpl<C> {
7372
// or those with untracked inputs
7473
// as their values cannot be reconstructed.
7574
}
76-
QueryOrigin::Derived(_) => {
77-
// Set the memo value to `None`.
78-
memo.value = None;
79-
}
75+
QueryOrigin::Derived(_) => delayed.clear_value(memo),
8076
}
81-
};
82-
83-
table.map_memo(memo_ingredient_index, map)
77+
}
8478
}
8579
}
8680

@@ -255,4 +249,7 @@ impl<V: Send + Sync + Any> crate::table::memo::Memo for Memo<V> {
255249
fn origin(&self) -> &QueryOrigin {
256250
&self.revisions.origin
257251
}
252+
fn clear_value(&mut self) {
253+
self.value = None;
254+
}
258255
}

src/ingredient.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::accumulator::accumulated_map::{AccumulatedMap, InputAccumulatedValues
55
use crate::cycle::{CycleHeadKind, CycleHeads, CycleRecoveryStrategy};
66
use crate::function::VerifyResult;
77
use crate::loom::sync::Arc;
8-
use crate::plumbing::IngredientIndices;
8+
use crate::plumbing::{IngredientIndices, MemoDropSender};
99
use crate::table::memo::MemoTableTypes;
1010
use crate::table::Table;
1111
use crate::zalsa::{transmute_data_mut_ptr, transmute_data_ptr, IngredientIndex, Zalsa};
@@ -33,6 +33,7 @@ pub trait Jar: Any {
3333
zalsa: &Zalsa,
3434
first_index: IngredientIndex,
3535
dependencies: IngredientIndices,
36+
memo_drop_sender: MemoDropSender,
3637
) -> Vec<Box<dyn Ingredient>>
3738
where
3839
Self: Sized;

0 commit comments

Comments
 (0)