Skip to content

Commit e53c2ab

Browse files
committed
add option to track heap memory usage of memos
1 parent c145596 commit e53c2ab

32 files changed

+310
-96
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ expect-test = "1.5.0"
6363
rustversion = "1.0"
6464
test-log = { version = "0.2.11", features = ["trace"] }
6565
trybuild = "1.0"
66+
get-size2 = { version = "0.4.1", features = ["derive"] }
6667

6768
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))'.dev-dependencies]
6869
tikv-jemallocator = "0.6.0"

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ macro_rules! setup_tracked_fn {
5555
// If true, the input needs an interner (because it has >1 argument).
5656
needs_interner: $needs_interner:tt,
5757

58+
// The function used to implement `C::heap_size`.
59+
heap_size_fn: $($heap_size_fn:path)?,
60+
5861
// LRU capacity (a literal, maybe 0)
5962
lru: $lru:tt,
6063

@@ -196,6 +199,12 @@ macro_rules! setup_tracked_fn {
196199

197200
$($values_equal)+
198201

202+
$(
203+
fn heap_size(value: &Self::Output<'_>) -> usize {
204+
$heap_size_fn(value)
205+
}
206+
)?
207+
199208
fn execute<$db_lt>($db: &$db_lt Self::DbView, ($($input_id),*): ($($interned_input_ty),*)) -> Self::Output<$db_lt> {
200209
$($assert_return_type_is_update)*
201210

components/salsa-macros/src/accumulator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl AllowedOptions for Accumulator {
4545
const CONSTRUCTOR_NAME: bool = false;
4646
const ID: bool = false;
4747
const REVISIONS: bool = false;
48+
const HEAP_SIZE: bool = false;
4849
}
4950

5051
struct StructMacro {

components/salsa-macros/src/input.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ impl crate::options::AllowedOptions for InputStruct {
6464
const ID: bool = false;
6565

6666
const REVISIONS: bool = false;
67+
68+
const HEAP_SIZE: bool = false;
6769
}
6870

6971
impl SalsaStructAllowedOptions for InputStruct {

components/salsa-macros/src/interned.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ impl crate::options::AllowedOptions for InternedStruct {
6464
const ID: bool = true;
6565

6666
const REVISIONS: bool = true;
67+
68+
const HEAP_SIZE: bool = false;
6769
}
6870

6971
impl SalsaStructAllowedOptions for InternedStruct {

components/salsa-macros/src/options.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ pub(crate) struct Options<A: AllowedOptions> {
9999
/// This is stored as a `syn::Expr` to support `usize::MAX`.
100100
pub revisions: Option<syn::Expr>,
101101

102+
/// The `heap_size = <path>` option can be used to track heap memory usage of memoized
103+
/// values.
104+
///
105+
/// If this is `Some`, the value is the provided `heap_size` function.
106+
pub heap_size_fn: Option<syn::Path>,
107+
102108
/// Remember the `A` parameter, which plays no role after parsing.
103109
phantom: PhantomData<A>,
104110
}
@@ -123,6 +129,7 @@ impl<A: AllowedOptions> Default for Options<A> {
123129
singleton: Default::default(),
124130
id: Default::default(),
125131
revisions: Default::default(),
132+
heap_size_fn: Default::default(),
126133
}
127134
}
128135
}
@@ -145,6 +152,7 @@ pub(crate) trait AllowedOptions {
145152
const CONSTRUCTOR_NAME: bool;
146153
const ID: bool;
147154
const REVISIONS: bool;
155+
const HEAP_SIZE: bool;
148156
}
149157

150158
type Equals = syn::Token![=];
@@ -392,6 +400,22 @@ impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
392400
"`revisions` option not allowed here",
393401
));
394402
}
403+
} else if ident == "heap_size" {
404+
if A::HEAP_SIZE {
405+
let _eq = Equals::parse(input)?;
406+
let path = syn::Path::parse(input)?;
407+
if let Some(old) = options.heap_size_fn.replace(path) {
408+
return Err(syn::Error::new(
409+
old.span(),
410+
"option `heap_size` provided twice",
411+
));
412+
}
413+
} else {
414+
return Err(syn::Error::new(
415+
ident.span(),
416+
"`heap_size` option not allowed here",
417+
));
418+
}
395419
} else {
396420
return Err(syn::Error::new(
397421
ident.span(),

components/salsa-macros/src/tracked_fn.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ impl crate::options::AllowedOptions for TrackedFn {
5757
const ID: bool = false;
5858

5959
const REVISIONS: bool = false;
60+
61+
const HEAP_SIZE: bool = true;
6062
}
6163

6264
struct Macro {
@@ -97,6 +99,7 @@ impl Macro {
9799
self.cycle_recovery()?;
98100
let is_specifiable = self.args.specify.is_some();
99101
let requires_update = self.args.non_update_return_type.is_none();
102+
let heap_size_fn = self.args.heap_size_fn.iter();
100103
let eq = if let Some(token) = &self.args.no_eq {
101104
if self.args.cycle_fn.is_some() {
102105
return Err(syn::Error::new_spanned(
@@ -217,6 +220,7 @@ impl Macro {
217220
is_specifiable: #is_specifiable,
218221
values_equal: {#eq},
219222
needs_interner: #needs_interner,
223+
heap_size_fn: #(#heap_size_fn)*,
220224
lru: #lru,
221225
return_mode: #return_mode,
222226
assert_return_type_is_update: { #assert_return_type_is_update },

components/salsa-macros/src/tracked_struct.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ impl crate::options::AllowedOptions for TrackedStruct {
6060
const ID: bool = false;
6161

6262
const REVISIONS: bool = false;
63+
64+
const HEAP_SIZE: bool = false;
6365
}
6466

6567
impl SalsaStructAllowedOptions for TrackedStruct {

src/database.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ impl dyn Database {
135135
}
136136

137137
#[cfg(feature = "salsa_unstable")]
138-
pub use memory_usage::{IngredientInfo, SlotInfo};
138+
pub use memory_usage::IngredientInfo;
139+
140+
#[cfg(feature = "salsa_unstable")]
141+
pub(crate) use memory_usage::{MemoInfo, SlotInfo};
139142

140143
#[cfg(feature = "salsa_unstable")]
141144
mod memory_usage {
@@ -171,8 +174,8 @@ mod memory_usage {
171174
/// Returns information about any memoized Salsa queries.
172175
///
173176
/// The returned map holds memory usage information for memoized values of a given query, keyed
174-
/// by its `(input, output)` type names.
175-
pub fn queries_info(&self) -> HashMap<(&'static str, &'static str), IngredientInfo> {
177+
/// by the query function name.
178+
pub fn queries_info(&self) -> HashMap<&'static str, IngredientInfo> {
176179
let mut queries = HashMap::new();
177180

178181
for input_ingredient in self.zalsa().ingredients() {
@@ -181,17 +184,15 @@ mod memory_usage {
181184
};
182185

183186
for input in input_info {
184-
for output in input.memos {
185-
let info = queries
186-
.entry((input.debug_name, output.debug_name))
187-
.or_insert(IngredientInfo {
188-
debug_name: output.debug_name,
189-
..Default::default()
190-
});
187+
for memo in input.memos {
188+
let info = queries.entry(memo.debug_name).or_insert(IngredientInfo {
189+
debug_name: memo.output.debug_name,
190+
..Default::default()
191+
});
191192

192193
info.count += 1;
193-
info.size_of_fields += output.size_of_fields;
194-
info.size_of_metadata += output.size_of_metadata;
194+
info.size_of_fields += memo.output.size_of_fields;
195+
info.size_of_metadata += memo.output.size_of_metadata;
195196
}
196197
}
197198
}
@@ -236,6 +237,12 @@ mod memory_usage {
236237
pub(crate) debug_name: &'static str,
237238
pub(crate) size_of_metadata: usize,
238239
pub(crate) size_of_fields: usize,
239-
pub(crate) memos: Vec<SlotInfo>,
240+
pub(crate) memos: Vec<MemoInfo>,
241+
}
242+
243+
/// Memory usage information about a particular memo.
244+
pub struct MemoInfo {
245+
pub(crate) debug_name: &'static str,
246+
pub(crate) output: SlotInfo,
240247
}
241248
}

src/function.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod memo;
3636
mod specify;
3737
mod sync;
3838

39-
pub type Memo<C> = memo::Memo<<C as Configuration>::Output<'static>>;
39+
pub type Memo<C> = memo::Memo<'static, C>;
4040

4141
pub trait Configuration: Any {
4242
const DEBUG_NAME: &'static str;
@@ -72,6 +72,11 @@ pub trait Configuration: Any {
7272
/// This is a no-op if the input to the function is a salsa struct.
7373
fn id_to_input(db: &Self::DbView, key: Id) -> Self::Input<'_>;
7474

75+
/// Returns the size of any heap allocations in the output value, in bytes.
76+
fn heap_size(_value: &Self::Output<'_>) -> usize {
77+
0
78+
}
79+
7580
/// Invoked when we need to compute the value for the given key, either because we've never
7681
/// computed it before or because the old one relied on inputs that have changed.
7782
///
@@ -181,8 +186,8 @@ where
181186
/// only cleared with `&mut self`.
182187
unsafe fn extend_memo_lifetime<'this>(
183188
&'this self,
184-
memo: &memo::Memo<C::Output<'this>>,
185-
) -> &'this memo::Memo<C::Output<'this>> {
189+
memo: &memo::Memo<'this, C>,
190+
) -> &'this memo::Memo<'this, C> {
186191
// SAFETY: the caller must guarantee that the memo will not be released before `&self`
187192
unsafe { std::mem::transmute(memo) }
188193
}
@@ -191,9 +196,9 @@ where
191196
&'db self,
192197
zalsa: &'db Zalsa,
193198
id: Id,
194-
mut memo: memo::Memo<C::Output<'db>>,
199+
mut memo: memo::Memo<'db, C>,
195200
memo_ingredient_index: MemoIngredientIndex,
196-
) -> &'db memo::Memo<C::Output<'db>> {
201+
) -> &'db memo::Memo<'db, C> {
197202
if let Some(tracked_struct_ids) = memo.revisions.tracked_struct_ids_mut() {
198203
tracked_struct_ids.shrink_to_fit();
199204
}

0 commit comments

Comments
 (0)