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" }
dvdplm marked this conversation as resolved.
Show resolved Hide resolved

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"
84 changes: 84 additions & 0 deletions parity-util-mem/derive/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

//! A crate for deriving the MallocSizeOf trait.
NikVolf marked this conversation as resolved.
Show resolved Hide resolved
//!
//! This is a copy of Servo malloc_size_of_derive code, modified to work with
//! our `parity_util_mem` library

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
63 changes: 63 additions & 0 deletions parity-util-mem/tests/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use parity_util_mem::{MallocSizeOf, MallocSizeOfExt};
NikVolf marked this conversation as resolved.
Show resolved Hide resolved

#[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);
}

dvdplm marked this conversation as resolved.
Show resolved Hide resolved
#[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);
}