Description
The Alloc::usable_size
method is, in general, used like this:
let required_layout = ...compute minimum layout required...;
let (l, u) = Alloc::usable_size(required_layout.size(), required_layout.align());
let layout = ...compute a new layout that makes use of l,u...;
let a = Alloc::alloc(layout)?; // allocate the memory
...track [l, u] as part of the allocation internally...
This has two big downsides.
The main issue is that usable_size
makes it impossible to obtain meaningful telemetry information about which allocations the program actually wants to perform, resulting in a program that is essentially "un-tuneable" from the allocator perspective.
For example, if one were to dump allocation statistics with jemalloc for a program that uses usable_size
, this program would appear to be using the allocator's size classes perfectly, independently of how bad these are.
The other issue is that code that uses usable_size
computes the allocation size classes twice, once in the usable_size
call, and once in the call to alloc
(alloc
just gets a layout, it then needs to find the size class appropriate for the layout, which is exactly what usable_size
already did, but alloc
does not know).
So IMO we should remove usable_size
, and instead, make the allocation functions always return the usable_size
, such that code like the above is now written as:
let required_layout = ...compute minimum layout required...;
let (ptr, usable_size) = Alloc::alloc(required_layout)?;
...track [required_layout.size(), usable_size] as part of the allocation internally...
This makes sure that allocators are always able to print accurate statistics of the requested allocators, which can then be used to tune allocator performance for particular applications, and allows allocators to perform the size class computation only once (e.g. jemalloc's smallocx
). Most ABIs do support two return registers in their calling convention, such that a pointer and a size can be always returned in most platforms without incurring any extra costs (e.g. like having to pass this via memory).
Allocators that do not support size class computation (e.g. glibc's malloc
), can just return the requested layout size in Rust code, which can be inlined. If the usable size of the return is not used, inlining can remove its computation for allocators that do need to perform an extra call to obtain it, as long as that function is readonly
(e.g. jemalloc's mallocx+nallocx
).
This means that all Alloc::..._excess
functions can be removed, since the normal functions do their job by default.
closes #13