🔎 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. The bird above is a Fauvette à
lunettes (Curruca conspicillata, Spectacled Warbler).
The classical Cargo
step! Add the following line to your
Cargo.toml
file:
[dependencies]
loupe = "0.1"
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 struct
s and enum
s.
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.
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!
MIT
License, see LICENSE
.