Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide #[derive(MallocSizeOf)] that is actually working #291

Merged
merged 12 commits into from
Dec 31, 2019
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ members = [
"primitive-types",
"ethereum-types",
"ethbloom",
"parity-util-mem/derive"
]
2 changes: 1 addition & 1 deletion parity-util-mem/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ edition = "2018"

[dependencies]
cfg-if = "0.1.10"
malloc_size_of_derive = "0.1.1"
dlmalloc = { version = "0.1.3", features = ["global"], optional = true }
wee_alloc = { version = "0.4.5", optional = true }
# from https://github.com/microsoft/mimalloc:
Expand All @@ -19,6 +18,7 @@ wee_alloc = { version = "0.4.5", optional = true }
# The performance penalty is only around 3% on average over our benchmarks.
mimallocator = { version = "0.1.3", features = ["secure"], optional = true }
mimalloc-sys = { version = "0.1.6", optional = true }
parity-util-mem-derive = { path = "derive", version = "0.1" }

smallvec = { version = "1.0.0", optional = true }
ethereum-types = { version = "0.8.0", optional = true, path = "../ethereum-types" }
Expand Down
16 changes: 16 additions & 0 deletions parity-util-mem/derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "parity-util-mem-derive"
version = "0.1.0"
authors = ["Parity Technologies"]
license = "MIT"
description = "Crate for memory reporting"
repository = "https://github.com/paritytech/pariry-common/parity-util-mem/derive"

[lib]
path = "lib.rs"
proc-macro = true

[dependencies]
proc-macro2 = "1"
syn = { version = "1", features = ["full"] }
synstructure = "0.12"
65 changes: 65 additions & 0 deletions parity-util-mem/derive/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! A crate for deriving the MallocSizeOf trait.

extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate synstructure;

#[cfg(not(test))]
decl_derive!([MallocSizeOf, attributes(ignore_malloc_size_of)] => malloc_size_of_derive);

fn malloc_size_of_derive(s: synstructure::Structure) -> proc_macro2::TokenStream {
let match_body = s.each(|binding| {
let ignore = binding.ast().attrs.iter().any(|attr| match attr.parse_meta().unwrap() {
syn::Meta::Path(ref path) | syn::Meta::List(syn::MetaList { ref path, .. })
if path.is_ident("ignore_malloc_size_of") =>
{
panic!(
"#[ignore_malloc_size_of] should have an explanation, \
e.g. #[ignore_malloc_size_of = \"because reasons\"]"
);
}
syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident("ignore_malloc_size_of") => true,
_ => false,
});
if ignore {
None
} else if let syn::Type::Array(..) = binding.ast().ty {
Some(quote! {
for item in #binding.iter() {
sum += parity_util_mem::MallocSizeOf::size_of(item, ops);
}
})
} else {
Some(quote! {
sum += parity_util_mem::MallocSizeOf::size_of(#binding, ops);
})
}
});

let ast = s.ast();
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let mut where_clause = where_clause.unwrap_or(&parse_quote!(where)).clone();
for param in ast.generics.type_params() {
let ident = &param.ident;
where_clause.predicates.push(parse_quote!(#ident: parity_util_mem::MallocSizeOf));
}

let tokens = quote! {
impl #impl_generics parity_util_mem::MallocSizeOf for #name #ty_generics #where_clause {
#[inline]
#[allow(unused_variables, unused_mut, unreachable_code)]
fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize {
let mut sum = 0;
match *self {
#match_body
}
sum
}
}
};

tokens
}
6 changes: 6 additions & 0 deletions parity-util-mem/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ malloc_size_of_is_0!(std::time::Duration);

malloc_size_of_is_0!(U64, U128, U256, U512, H32, H64, H128, H160, H256, H264, H512, H520, Bloom);

malloc_size_of_is_0!(
[u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 9], [u8; 10], [u8; 11], [u8; 12],
[u8; 13], [u8; 14], [u8; 15], [u8; 16], [u8; 17], [u8; 18], [u8; 19], [u8; 20], [u8; 21], [u8; 22], [u8; 23],
[u8; 24], [u8; 25], [u8; 26], [u8; 27], [u8; 28], [u8; 29], [u8; 30], [u8; 31], [u8; 32]
);

macro_rules! impl_smallvec {
($size: expr) => {
impl<T> MallocSizeOf for SmallVec<[T; $size]>
Expand Down
5 changes: 2 additions & 3 deletions parity-util-mem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
#[cfg(not(feature = "std"))]
extern crate alloc;

use malloc_size_of_derive as malloc_size_derive;

cfg_if::cfg_if! {
if #[cfg(all(
feature = "jemalloc-global",
Expand Down Expand Up @@ -72,7 +70,8 @@ pub mod impls;

pub use allocators::MallocSizeOfExt;
pub use malloc_size::{MallocSizeOf, MallocSizeOfOps};
pub use malloc_size_derive::*;

pub use parity_util_mem_derive::*;

#[cfg(feature = "std")]
#[cfg(test)]
Expand Down
20 changes: 6 additions & 14 deletions parity-util-mem/src/malloc_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
//! measured as well as the thing it points to. E.g.
//! `<Box<_> as MallocSizeOf>::size_of(field, ops)`.

//! This is an extended (for own internal needs) version of the Servo internal malloc_size crate.
//! We should occasionally track the upstream changes/fixes and reintroduce them here, be they applicable.
//! This is an extended version of the Servo internal malloc_size crate.
//! We should occasionally track the upstream changes/fixes and reintroduce them here, whenever applicable.

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
Expand Down Expand Up @@ -426,11 +426,7 @@ where
}

#[cfg(feature = "std")]
impl<K, V, S> MallocShallowSizeOf for std::collections::HashMap<K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
impl<K, V, S> MallocShallowSizeOf for std::collections::HashMap<K, V, S> {
fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
// See the implementation for std::collections::HashSet for details.
if ops.has_malloc_enclosing_size_of() {
Expand All @@ -444,9 +440,8 @@ where
#[cfg(feature = "std")]
impl<K, V, S> MallocSizeOf for std::collections::HashMap<K, V, S>
where
K: Eq + Hash + MallocSizeOf,
K: MallocSizeOf,
V: MallocSizeOf,
S: BuildHasher,
{
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let mut n = self.shallow_size_of(ops);
Expand All @@ -458,10 +453,7 @@ where
}
}

impl<K, V> MallocShallowSizeOf for rstd::collections::BTreeMap<K, V>
where
K: Eq + Hash,
{
impl<K, V> MallocShallowSizeOf for rstd::collections::BTreeMap<K, V> {
fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
if ops.has_malloc_enclosing_size_of() {
self.values().next().map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) })
Expand All @@ -473,7 +465,7 @@ where

impl<K, V> MallocSizeOf for rstd::collections::BTreeMap<K, V>
where
K: Eq + Hash + MallocSizeOf,
K: MallocSizeOf,
V: MallocSizeOf,
{
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
Expand Down
47 changes: 47 additions & 0 deletions parity-util-mem/tests/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use parity_util_mem::{MallocSizeOf, MallocSizeOfExt};

#[test]
#[cfg(feature = "std")]
fn derive_vec() {
#[derive(MallocSizeOf)]
struct Trivia {
v: Vec<u8>,
}

let t = Trivia { v: vec![0u8; 1024] };

assert!(t.malloc_size_of() > 1000);
}

#[test]
#[cfg(feature = "std")]
fn derive_hashmap() {
#[derive(MallocSizeOf, Default)]
struct Trivia {
hm: std::collections::HashMap<u64, Vec<u8>>,
}

let mut t = Trivia::default();

t.hm.insert(1, vec![0u8; 2048]);

assert!(t.malloc_size_of() > 2000);
}

#[test]
#[cfg(feature = "std")]
fn derive_ignore() {
#[derive(MallocSizeOf, Default)]
struct Trivia {
hm: std::collections::HashMap<u64, Vec<u8>>,
#[ignore_malloc_size_of = "I don't like vectors"]
v: Vec<u8>,
}

let mut t = Trivia::default();

t.hm.insert(1, vec![0u8; 2048]);
t.v = vec![0u8; 1024];

assert!(t.malloc_size_of() < 3000);
}