Skip to content

Commit fb4cb24

Browse files
authored
Persistent caching fixes (#982)
* reduce `serde` overhead * avoid adding dependencies on interned values where garbage collection is disabled * add compatibility with non-self-describing `serde` formats * reuse edge traversal allocation * correctly deserialize singleton inputs
1 parent b92180b commit fb4cb24

File tree

13 files changed

+315
-199
lines changed

13 files changed

+315
-199
lines changed

src/active_query.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,9 @@ impl ActiveQuery {
208208
} = self;
209209

210210
let origin = if untracked_read {
211-
QueryOrigin::derived_untracked(input_outputs.drain(..))
211+
QueryOrigin::derived_untracked(input_outputs.drain(..).collect())
212212
} else {
213-
QueryOrigin::derived(input_outputs.drain(..))
213+
QueryOrigin::derived(input_outputs.drain(..).collect())
214214
};
215215
disambiguator_map.clear();
216216

src/cycle.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ pub struct CycleHead {
104104

105105
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
106106
#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
107+
#[cfg_attr(feature = "persistence", serde(transparent))]
107108
pub struct IterationCount(u8);
108109

109110
impl IterationCount {

src/database.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ mod persistence {
160160

161161
use std::fmt;
162162

163-
use serde::de::{self, DeserializeSeed};
163+
use serde::de::{self, DeserializeSeed, SeqAccess};
164164
use serde::ser::SerializeMap;
165165

166166
impl dyn Database {
@@ -275,6 +275,21 @@ mod persistence {
275275
formatter.write_str("struct Database")
276276
}
277277

278+
fn visit_seq<V>(self, mut seq: V) -> Result<(), V::Error>
279+
where
280+
V: SeqAccess<'de>,
281+
{
282+
let mut runtime = seq
283+
.next_element()?
284+
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
285+
let () = seq
286+
.next_element_seed(DeserializeIngredients(self.0))?
287+
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
288+
289+
self.0.runtime_mut().deserialize_from(&mut runtime);
290+
Ok(())
291+
}
292+
278293
fn visit_map<V>(self, mut map: V) -> Result<(), V::Error>
279294
where
280295
V: serde::de::MapAccess<'de>,

src/durability.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,28 @@
1717
/// configuration, the source from library crates, or other things
1818
/// that are unlikely to be edited.
1919
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
20-
#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
2120
pub struct Durability(DurabilityVal);
2221

22+
#[cfg(feature = "persistence")]
23+
impl serde::Serialize for Durability {
24+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
25+
where
26+
S: serde::Serializer,
27+
{
28+
serde::Serialize::serialize(&(self.0 as u8), serializer)
29+
}
30+
}
31+
32+
#[cfg(feature = "persistence")]
33+
impl<'de> serde::Deserialize<'de> for Durability {
34+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35+
where
36+
D: serde::Deserializer<'de>,
37+
{
38+
u8::deserialize(deserializer).map(|value| Self(DurabilityVal::from(value)))
39+
}
40+
}
41+
2342
impl std::fmt::Debug for Durability {
2443
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2544
if f.alternate() {
@@ -38,7 +57,6 @@ impl std::fmt::Debug for Durability {
3857

3958
// We use an enum here instead of a u8 for niches.
4059
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
41-
#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
4260
enum DurabilityVal {
4361
Low = 0,
4462
Medium = 1,

src/function.rs

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,26 @@ mod persistence {
541541
{
542542
let Self { ingredient, zalsa } = self;
543543

544-
let mut map = serializer.serialize_map(None)?;
544+
let count = <C::SalsaStruct<'_> as SalsaStructInDb>::entries(zalsa)
545+
.filter(|entry| {
546+
let memo_ingredient_index = ingredient
547+
.memo_ingredient_indices
548+
.get(entry.ingredient_index());
549+
550+
let memo = ingredient.get_memo_from_table_for(
551+
zalsa,
552+
entry.key_index(),
553+
memo_ingredient_index,
554+
);
555+
556+
memo.is_some_and(|memo| memo.should_serialize())
557+
})
558+
.count();
559+
560+
let mut map = serializer.serialize_map(Some(count))?;
561+
562+
let mut visited_edges = FxHashSet::default();
563+
let mut flattened_edges = FxIndexSet::default();
545564

546565
for entry in <C::SalsaStruct<'_> as SalsaStructInDb>::entries(zalsa) {
547566
let memo_ingredient_index = ingredient
@@ -558,10 +577,24 @@ mod persistence {
558577
// Flatten the dependencies of this query down to the base inputs.
559578
let flattened_origin = match memo.revisions.origin.as_ref() {
560579
QueryOriginRef::Derived(edges) => {
561-
QueryOrigin::derived(flatten_edges(zalsa, edges))
580+
collect_minimum_serialized_edges(
581+
zalsa,
582+
edges,
583+
&mut visited_edges,
584+
&mut flattened_edges,
585+
);
586+
587+
QueryOrigin::derived(flattened_edges.drain(..).collect())
562588
}
563589
QueryOriginRef::DerivedUntracked(edges) => {
564-
QueryOrigin::derived_untracked(flatten_edges(zalsa, edges))
590+
collect_minimum_serialized_edges(
591+
zalsa,
592+
edges,
593+
&mut visited_edges,
594+
&mut flattened_edges,
595+
);
596+
597+
QueryOrigin::derived_untracked(flattened_edges.drain(..).collect())
565598
}
566599
QueryOriginRef::Assigned(key) => {
567600
let dependency = zalsa.lookup_ingredient(key.ingredient_index());
@@ -588,6 +621,8 @@ mod persistence {
588621
);
589622

590623
map.serialize_entry(&key, &memo)?;
624+
625+
visited_edges.clear();
591626
}
592627
}
593628

@@ -596,11 +631,12 @@ mod persistence {
596631
}
597632

598633
// Flatten the dependency edges before serialization.
599-
fn flatten_edges(zalsa: &Zalsa, edges: &[QueryEdge]) -> FxIndexSet<QueryEdge> {
600-
let mut visited_edges = FxHashSet::default();
601-
let mut flattened_edges =
602-
FxIndexSet::with_capacity_and_hasher(edges.len(), Default::default());
603-
634+
fn collect_minimum_serialized_edges(
635+
zalsa: &Zalsa,
636+
edges: &[QueryEdge],
637+
visited_edges: &mut FxHashSet<QueryEdge>,
638+
flattened_edges: &mut FxIndexSet<QueryEdge>,
639+
) {
604640
for &edge in edges {
605641
let dependency = zalsa.lookup_ingredient(edge.key().ingredient_index());
606642

@@ -612,13 +648,11 @@ mod persistence {
612648
dependency.collect_minimum_serialized_edges(
613649
zalsa,
614650
edge,
615-
&mut flattened_edges,
616-
&mut visited_edges,
651+
flattened_edges,
652+
visited_edges,
617653
);
618654
}
619655
}
620-
621-
flattened_edges
622656
}
623657

624658
pub struct DeserializeIngredient<'db, C>
@@ -659,7 +693,7 @@ mod persistence {
659693
{
660694
let DeserializeIngredient { zalsa, ingredient } = self;
661695

662-
while let Some((key, memo)) = access.next_entry::<String, Memo<C>>()? {
696+
while let Some((key, memo)) = access.next_entry::<&str, Memo<C>>()? {
663697
let (ingredient_index, id) = key
664698
.split_once(':')
665699
.ok_or_else(|| de::Error::custom("invalid database key"))?;

src/function/memo.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,9 @@ mod persistence {
367367

368368
/// A reference to the fields of a [`Memo`], with its [`QueryRevisions`] transformed.
369369
pub(crate) struct MappedMemo<'memo, 'db, C: Configuration> {
370-
value: Option<&'memo C::Output<'db>>,
371-
verified_at: AtomicRevision,
372-
revisions: MappedQueryRevisions<'memo>,
370+
pub(crate) value: Option<&'memo C::Output<'db>>,
371+
pub(crate) verified_at: AtomicRevision,
372+
pub(crate) revisions: MappedQueryRevisions<'memo>,
373373
}
374374

375375
impl<'db, C: Configuration> Memo<'db, C> {
@@ -437,6 +437,7 @@ mod persistence {
437437
D: serde::Deserializer<'de>,
438438
{
439439
#[derive(Deserialize)]
440+
#[serde(rename = "Memo")]
440441
pub struct DeserializeMemo<C: Configuration> {
441442
#[serde(bound = "C: Configuration")]
442443
value: DeserializeValue<C>,

src/input.rs

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,16 @@ impl<C: Configuration> IngredientImpl<C> {
152152
durabilities: C::Durabilities,
153153
) -> C::Struct {
154154
let id = self.singleton.with_scope(|| {
155-
let (id, _) = zalsa_local.allocate(zalsa, self.ingredient_index, |_| Value::<C> {
156-
fields,
157-
revisions,
158-
durabilities,
159-
// SAFETY: We only ever access the memos of a value that we allocated through
160-
// our `MemoTableTypes`.
161-
memos: unsafe { MemoTable::new(self.memo_table_types()) },
162-
});
163-
164-
id
155+
zalsa_local
156+
.allocate(zalsa, self.ingredient_index, |_| Value::<C> {
157+
fields,
158+
revisions,
159+
durabilities,
160+
// SAFETY: We only ever access the memos of a value that we allocated through
161+
// our `MemoTableTypes`.
162+
memos: unsafe { MemoTable::new(self.memo_table_types()) },
163+
})
164+
.0
165165
});
166166

167167
FromIdWithDb::from_id(id, zalsa)
@@ -440,10 +440,11 @@ where
440440
mod persistence {
441441
use std::fmt;
442442

443-
use serde::ser::SerializeMap;
443+
use serde::ser::{SerializeMap, SerializeStruct};
444444
use serde::{de, Deserialize};
445445

446446
use super::{Configuration, IngredientImpl, Value};
447+
use crate::input::singleton::SingletonChoice;
447448
use crate::plumbing::Ingredient;
448449
use crate::table::memo::MemoTable;
449450
use crate::zalsa::Zalsa;
@@ -467,7 +468,8 @@ mod persistence {
467468
{
468469
let Self { zalsa, .. } = self;
469470

470-
let mut map = serializer.serialize_map(None)?;
471+
let count = zalsa.table().slots_of::<Value<C>>().count();
472+
let mut map = serializer.serialize_map(Some(count))?;
471473

472474
for (id, value) in zalsa.table().slots_of::<Value<C>>() {
473475
map.serialize_entry(&id.as_bits(), value)?;
@@ -485,21 +487,7 @@ mod persistence {
485487
where
486488
S: serde::Serializer,
487489
{
488-
let mut map = serializer.serialize_map(None)?;
489-
490-
struct SerializeFields<'db, C: Configuration>(&'db C::Fields);
491-
492-
impl<C> serde::Serialize for SerializeFields<'_, C>
493-
where
494-
C: Configuration,
495-
{
496-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
497-
where
498-
S: serde::Serializer,
499-
{
500-
C::serialize(self.0, serializer)
501-
}
502-
}
490+
let mut value = serializer.serialize_struct("Value", 3)?;
503491

504492
let Value {
505493
fields,
@@ -508,11 +496,25 @@ mod persistence {
508496
memos: _,
509497
} = self;
510498

511-
map.serialize_entry(&"durabilities", &durabilities)?;
512-
map.serialize_entry(&"revisions", &revisions)?;
513-
map.serialize_entry(&"fields", &SerializeFields::<C>(fields))?;
499+
value.serialize_field("durabilities", &durabilities)?;
500+
value.serialize_field("revisions", &revisions)?;
501+
value.serialize_field("fields", &SerializeFields::<C>(fields))?;
514502

515-
map.end()
503+
value.end()
504+
}
505+
}
506+
507+
struct SerializeFields<'db, C: Configuration>(&'db C::Fields);
508+
509+
impl<C> serde::Serialize for SerializeFields<'_, C>
510+
where
511+
C: Configuration,
512+
{
513+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
514+
where
515+
S: serde::Serializer,
516+
{
517+
C::serialize(self.0, serializer)
516518
}
517519
}
518520

@@ -577,13 +579,14 @@ mod persistence {
577579
// Initialize the slot.
578580
//
579581
// SAFETY: We have a mutable reference to the database.
580-
let (allocated_id, _) = unsafe {
582+
let allocated_id = ingredient.singleton.with_scope(|| unsafe {
581583
zalsa
582584
.table()
583585
.page(page_idx)
584586
.allocate(page_idx, |_| value)
585587
.unwrap_or_else(|_| panic!("serialized an invalid `Id`: {id:?}"))
586-
};
588+
.0
589+
});
587590

588591
assert_eq!(
589592
allocated_id, id,
@@ -596,6 +599,7 @@ mod persistence {
596599
}
597600

598601
#[derive(Deserialize)]
602+
#[serde(rename = "Value")]
599603
pub struct DeserializeValue<C: Configuration> {
600604
durabilities: C::Durabilities,
601605
revisions: C::Revisions,

0 commit comments

Comments
 (0)