Expand description
🔎 loupe is a set of tools to analyse and to profile Rust
code. For the moment, it only provides tools about memory
usage. It’s mostly driven by
Wasmer’s needs, but feel
free to propose new features!
loupe is a French word to express magnifying glass, and can be
pronounced exactly like loop.
Memory Usage
loupe provides the MemoryUsage trait. It allows to know the
size of a value in bytes, recursively. So it traverses most of
the types (some are missing, feel free to contribute!), and its
fields or variants as deep as possible. Hopefully, it tracks
already visited values so that it doesn’t enter an infinite
loupe loop. The trait looks like this:
pub trait MemoryUsage {
fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize;
}loupe provides a size_of_val function that is a close sibling
of
std::mem::size_of_val. It
can be used the same way.
loupe exports its best companion: handful procedural macros from
the loupe-derive crate, to
automatically implement the MemoryUsage trait if possible, on
structs and enums.
Thus, one only needs to write:
use loupe::MemoryUsage;
use std::mem;
#[derive(MemoryUsage)]
struct S {
x: Vec<i32>,
y: Vec<i32>,
}
fn main() {
let s = S {
x: vec![1, 2, 3],
y: vec![1, 2, 3],
};
assert_eq!(48, mem::size_of_val(&s));
assert_eq!(72, loupe::size_of_val(&s));
}In the example above, we see that each elements of Vec<i32> has
been counted in the size of the value s. In
Wasmer, it is possible to
get the size of an Instance, which traverses Module, Store,
Engine, Compiler etc. It’s an entire tree of values that is
traversed and the size of each value is summed.
Opinionated implementations
Even if MemoryUsage is already implemented for common types,
some types are missing. We happily welcome more implementations!
However, implementations of MemoryUsage:
- must never alter the values,
- must never panic of fail,
- must be deterministic as much as possible (ideally, everytime).
In the same spirit, our implementation of MemoryUsage for
*const T or *mut T (and other pointer types, like NonNull,
UnsafeCell etc.) just returns the size of the pointer, but it
doesn’t dereference the pointer as it’s unsafe. It doesn’t mean
one must not do that: It’s totally possible if it’s sure that the
pointer can be safely dereferenced.
Remember that a user can implement MemoryUsage by hand; no
need to try to have a default implementation for all the standard
types.
Finally, our implementations are certainly not perfect! Feel free to challenge it and come to discuss!
Macros
Alias to assert_eq!(loupe::MemoryUsage::size_of_val(&$value), $expected).
Constants
Size of a pointer for the compilation target.
Traits
Traverse a value and collect its memory usage.
Represent a bucket that can track memory addresses that have
already been visited by MemoryUsage.
Functions
Returns the size of the pointer-to value in bytes. The size is
calculated with MemoryUsage::size_of_val.
Derive Macros
Procedural macro to implement the loupe::MemoryUsage trait
automatically for structs and enums.