Skip to content

Commit 3c1c0fc

Browse files
ndmitchellfacebook-github-bot
authored andcommitted
Add extra_memory function
Summary: For profiling, we can observe how much memory a Value stores on the Starlark heap, but it's very difficult to see what memory the Starlark heap points at. Add an extra function to give more accurate memory profiling. This can never be perfect, think of things like Arc which are shared, but it's an improvement over the status quo. Next steps will be to integrate this into heap profiling modes. Reviewed By: krallin Differential Revision: D30866799 fbshipit-source-id: 99aa514e29de441422fa495ed27ba0a88a021fea
1 parent 0019286 commit 3c1c0fc

File tree

11 files changed

+68
-6
lines changed

11 files changed

+68
-6
lines changed

starlark/src/collections/small_map.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,21 @@ impl<K, V> SmallMap<K, V> {
569569
}
570570
}
571571

572+
/// Give a best guess as to how much heap memory is being used.
573+
/// Used internally, but not exported as this isn't a usual API.
574+
pub(crate) fn extra_memory(&self) -> usize {
575+
match &self.state {
576+
MapHolder::Empty => 0,
577+
MapHolder::Vec(x) => x.capacity() * mem::size_of::<(K, V)>(),
578+
MapHolder::Map(x) => {
579+
// For each entry, IndexMap stores the (K,V), a usize in the hash table, and about 1 byte overhead.
580+
// Not canonical details, just based on the current implementation.
581+
let factor = mem::size_of::<(K, V)>() + mem::size_of::<usize>() + 1;
582+
factor * x.capacity()
583+
}
584+
}
585+
}
586+
572587
fn upgrade_empty_to_vec(&mut self) -> &mut VecMap<K, V> {
573588
self.state = MapHolder::Vec(VecMap::default());
574589
if let MapHolder::Vec(ref mut v) = self.state {

starlark/src/values/layout/avalue.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ impl<'v, Mode: 'static, T: StarlarkValue<'v>> StarlarkValue<'v> for Wrapper<Mode
331331
fn compare(&self, other: Value<'v>) -> anyhow::Result<Ordering> {
332332
self.1.compare(other)
333333
}
334+
fn extra_memory(&self) -> usize {
335+
self.1.extra_memory()
336+
}
334337
fn invoke(
335338
&self,
336339
me: Value<'v>,

starlark/src/values/traits.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,13 @@ pub trait StarlarkValue<'v>: 'v + AnyLifetime<'v> + AsStarlarkValue<'v> + Debug
444444
}
445445
}
446446

447+
/// Return how much extra memory is consumed by this data type, in bytes, in addition to the
448+
/// direct `size_of` measurements. Used for profiling, so best effort rather than precise. Defaults to 0.
449+
/// Should not reported any memory held on to by a [`Value`].
450+
fn extra_memory(&self) -> usize {
451+
0
452+
}
453+
447454
/// Compare `self` with `other` for equality.
448455
/// Should only return an error on excessive recursion.
449456
fn equals(&self, _other: Value<'v>) -> anyhow::Result<bool> {

starlark/src/values/types/dict.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ where
320320
}
321321
}
322322

323+
fn extra_memory(&self) -> usize {
324+
self.0.content().extra_memory()
325+
}
326+
323327
fn length(&self) -> anyhow::Result<i32> {
324328
Ok(self.0.content().len() as i32)
325329
}

starlark/src/values/types/enumeration.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ where
221221
self.constructor.invoke(location, args, eval)
222222
}
223223

224+
fn extra_memory(&self) -> usize {
225+
let typ = self.typ.as_aref();
226+
typ.as_ref().map_or(0, |s| s.capacity()) + self.elements.extra_memory()
227+
}
228+
224229
fn length(&self) -> anyhow::Result<i32> {
225230
Ok(self.elements.len() as i32)
226231
}

starlark/src/values/types/function.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ impl<'v> StarlarkValue<'v> for NativeFunction {
165165
})
166166
}
167167

168+
fn extra_memory(&self) -> usize {
169+
self.name.capacity()
170+
}
171+
168172
fn get_attr(&self, attribute: &str, _heap: &'v Heap) -> Option<Value<'v>> {
169173
if let Some(s) = &self.typ {
170174
if attribute == "type" {

starlark/src/values/types/list.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use std::{
4343
fmt::Debug,
4444
intrinsics::unlikely,
4545
marker::PhantomData,
46+
mem,
4647
ops::Deref,
4748
};
4849

@@ -221,13 +222,13 @@ impl FrozenList {
221222
}
222223

223224
trait ListLike<'v>: Debug {
224-
fn content(&self) -> ARef<[Value<'v>]>;
225+
fn content(&self) -> ARef<Vec<Value<'v>>>;
225226
fn set_at(&self, i: usize, v: Value<'v>) -> anyhow::Result<()>;
226227
}
227228

228229
impl<'v> ListLike<'v> for RefCell<List<'v>> {
229-
fn content(&self) -> ARef<[Value<'v>]> {
230-
ARef::new_ref(Ref::map(self.borrow(), |x| x.content.as_slice()))
230+
fn content(&self) -> ARef<Vec<Value<'v>>> {
231+
ARef::new_ref(Ref::map(self.borrow(), |x| &x.content))
231232
}
232233

233234
fn set_at(&self, i: usize, v: Value<'v>) -> anyhow::Result<()> {
@@ -242,8 +243,8 @@ impl<'v> ListLike<'v> for RefCell<List<'v>> {
242243
}
243244

244245
impl<'v> ListLike<'v> for FrozenList {
245-
fn content(&self) -> ARef<[Value<'v>]> {
246-
ARef::new_ptr(coerce_ref(&self.content).as_slice())
246+
fn content(&self) -> ARef<Vec<Value<'v>>> {
247+
ARef::new_ptr(coerce_ref(&self.content))
247248
}
248249

249250
fn set_at(&self, _i: usize, _v: Value<'v>) -> anyhow::Result<()> {
@@ -313,6 +314,10 @@ where
313314
Ok(self.0.content()[i])
314315
}
315316

317+
fn extra_memory(&self) -> usize {
318+
self.0.content().capacity() * mem::size_of::<Value>()
319+
}
320+
316321
fn length(&self) -> anyhow::Result<i32> {
317322
Ok(self.0.content().len() as i32)
318323
}

starlark/src/values/types/record.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ where
294294
self.constructor.invoke(location, args, eval)
295295
}
296296

297+
fn extra_memory(&self) -> usize {
298+
// We don't capture the memory beneath the TypeCompiled, since we don't know how big
299+
// those closures are.
300+
let typ = self.typ.as_aref();
301+
typ.as_ref().map_or(0, |s| s.capacity()) + self.fields.extra_memory()
302+
}
303+
297304
fn dir_attr(&self) -> Vec<String> {
298305
vec!["type".to_owned()]
299306
}

starlark/src/values/types/string.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,10 @@ impl<'v> StarlarkValue<'v> for StarlarkStr {
293293
}
294294
}
295295

296+
fn extra_memory(&self) -> usize {
297+
self.len
298+
}
299+
296300
fn length(&self) -> anyhow::Result<i32> {
297301
Ok(fast_string::len(self.unpack()) as i32)
298302
}

starlark/src/values/types/structs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ where
124124
RES.methods(crate::stdlib::structs::struct_methods)
125125
}
126126

127+
fn extra_memory(&self) -> usize {
128+
self.fields.extra_memory()
129+
}
130+
127131
fn to_json(&self) -> anyhow::Result<String> {
128132
let mut s = "{".to_owned();
129133
s += &self

0 commit comments

Comments
 (0)