Skip to content
This repository was archived by the owner on Jul 10, 2023. It is now read-only.

Commit 50face8

Browse files
committed
Implement a lower bound for Arc<T> and Rc<T>
This crate has always given a lower bound of the actual heap memory usage: we use `#[ignore_heap_size_of]` when not doing it is not practical. This fix #37 with one of the ideas given in the discussion there: return an accurate result when the reference count is 1 (and ownership is in fact not shared), or zero otherwise. However, since APIs to access reference counts are not `#[stable]` yet (see rust-lang/rust#28356), so always return zero when unstable APIs are not enabled.
1 parent 3b8141a commit 50face8

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

src/lib.rs

+37-13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
//! Data structure measurement.
66
7+
#![cfg_attr(feature = "unstable", feature(rc_counts, arc_counts))]
8+
79
#[cfg(target_os = "windows")]
810
extern crate kernel32;
911

@@ -16,7 +18,7 @@ use std::collections::{BTreeMap, HashMap, LinkedList, VecDeque};
1618
use std::hash::BuildHasher;
1719
use std::hash::Hash;
1820
use std::marker::PhantomData;
19-
use std::mem::size_of;
21+
use std::mem::{size_of, size_of_val};
2022
use std::net::{Ipv4Addr, Ipv6Addr};
2123
use std::os::raw::c_void;
2224
use std::sync::Arc;
@@ -45,7 +47,7 @@ unsafe fn heap_size_of_impl(ptr: *const c_void) -> usize {
4547
// this function doesn't modify the contents of the block that `ptr` points to, so we use
4648
// `*const c_void` here.
4749
extern "C" {
48-
#[cfg_attr(any(prefixed_jemalloc, target_os = "macos", target_os = "android"), link_name = "je_malloc_usable_size")]
50+
#[cfg_attr(any(prefixed_jemalloc, target_os = "macos", target_os = "android"), link_name = "je_malloc_usable_size")]
4951
fn malloc_usable_size(ptr: *const c_void) -> usize;
5052
}
5153
malloc_usable_size(ptr)
@@ -150,12 +152,6 @@ impl<T: HeapSizeOf, U: HeapSizeOf> HeapSizeOf for (T, U) {
150152
}
151153
}
152154

153-
impl<T: HeapSizeOf> HeapSizeOf for Arc<T> {
154-
fn heap_size_of_children(&self) -> usize {
155-
(**self).heap_size_of_children()
156-
}
157-
}
158-
159155
impl<T: HeapSizeOf> HeapSizeOf for RefCell<T> {
160156
fn heap_size_of_children(&self) -> usize {
161157
self.borrow().heap_size_of_children()
@@ -185,16 +181,44 @@ impl<T: HeapSizeOf> HeapSizeOf for VecDeque<T> {
185181
}
186182
}
187183

188-
impl<T> HeapSizeOf for Vec<Rc<T>> {
184+
impl<T: HeapSizeOf> HeapSizeOf for Rc<T> {
185+
#[cfg(not(feature = "unstable"))]
189186
fn heap_size_of_children(&self) -> usize {
190-
// The fate of measuring Rc<T> is still undecided, but we still want to measure
191-
// the space used for storing them.
192-
unsafe {
193-
heap_size_of(self.as_ptr() as *const c_void)
187+
0
188+
}
189+
190+
#[cfg(feature = "unstable")]
191+
fn heap_size_of_children(&self) -> usize {
192+
if Rc::strong_count(self) == 1 {
193+
heap_size_of_rcbox::<T>(self)
194+
} else {
195+
0
194196
}
195197
}
196198
}
197199

200+
impl<T: HeapSizeOf> HeapSizeOf for Arc<T> {
201+
#[cfg(not(feature = "unstable"))]
202+
fn heap_size_of_children(&self) -> usize {
203+
0
204+
}
205+
206+
#[cfg(feature = "unstable")]
207+
fn heap_size_of_children(&self) -> usize {
208+
if Arc::strong_count(self) == 1 {
209+
heap_size_of_rcbox::<T>(self)
210+
} else {
211+
0
212+
}
213+
}
214+
}
215+
216+
/// Arc<T> and Rc<T> heap-allocate `(usize, usize, T)` with strong and weak refcounts.
217+
#[cfg(feature = "unstable")]
218+
fn heap_size_of_rcbox<T: ?Sized + HeapSizeOf>(x: &T) -> usize {
219+
size_of::<usize>() * 2 + size_of_val(x) + x.heap_size_of_children()
220+
}
221+
198222
#[cfg(feature = "unstable")]
199223
impl<K: HeapSizeOf, V: HeapSizeOf, S> HeapSizeOf for HashMap<K, V, S>
200224
where K: Eq + Hash, S: BuildHasher {

tests/tests.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
extern crate heapsize;
88

99
use heapsize::{HeapSizeOf, heap_size_of};
10+
use std::mem::size_of;
1011
use std::os::raw::c_void;
1112

1213
pub const EMPTY: *mut () = 0x1 as *mut ();
@@ -141,13 +142,14 @@ fn test_heap_size() {
141142
let x = Some(Box::new(0i64));
142143
assert_eq!(x.heap_size_of_children(), 8);
143144

144-
// Not on the heap.
145145
let x = ::std::sync::Arc::new(0i64);
146-
assert_eq!(x.heap_size_of_children(), 0);
146+
assert_eq!(x.heap_size_of_children(),
147+
if cfg!(feature = "unstable") { size_of::<usize>() * 2 + 8 } else { 0 });
147148

148-
// The `Arc` is not on the heap, but the Box is.
149+
// `ArcInner<Box<_>>` has 2 refcounts + a pointer on the heap.
149150
let x = ::std::sync::Arc::new(Box::new(0i64));
150-
assert_eq!(x.heap_size_of_children(), 8);
151+
assert_eq!(x.heap_size_of_children(),
152+
if cfg!(feature = "unstable") { size_of::<usize>() * 3 + 8 } else { 0 });
151153

152154
// Zero elements, no heap storage.
153155
let x: Vec<i64> = vec![];

0 commit comments

Comments
 (0)