rust-lang / wg-allocators

Home of the Allocators working group: Paving a path for a standard set of allocator traits to be used in collections!
http://bit.ly/hello-wg-allocators
205 stars 9 forks source link

Explicit function on AllocRef to provide allocation usable_size if supported #75

Open thomcc opened 3 years ago

thomcc commented 3 years ago

There's currently no easy way in the API to recover the allocation extent (aka the "usable_size", aka the maximum "fit" value, etc).

As one example, the allocation may have initially been performed by a type which doesn't have the ability to store the capacity information, for example allocating a Box<[T]> and converting to a Vec, as done in the vec![] macro.

Additionally, for many allocators, an explicit request to shrink an allocation will either do nothing, or only do something if it would change size classes. So it's completely plausible that the Vec::from code in Vec::from(a_vec.into_boxed_slice()) could have a lot to gain by querying the usable allocation size.

Right now, I believe the closest way of getting at this information is immediately invoking AllocRef::grow with a new_size parameter equal to layout.size() but this is opaque, potentially slow, and confusing.

IMO, Adding an explicit function to return the usable size if known would be better. Here's a bikeshed:

impl AllocRef {
    #[inline]
    unsafe fn usable_size(&self, ptr: NonNull<u8>) -> Option<usize> {
        None
    }
}

Importantly, returning a usable size value less than the previous maximum usable size does not reduce that maximum value. The intention here is: This function doesn't change the size of allocations although it can reveal that the size bound returned earlier was lower than the actual usable size.

This might seem too subtle, but it allows some currently allowed and potentially desirable implementations to remain legal and efficient:

For example: it's legal* to implement AllocRef::alloc by checking a malloc_good_size** function and passing its result to the allocation function, rather than calling malloc_usable_size on the allocation functions result.

The wording above intends to continue to allow that, and allow AllocRef::usable_size to be implemented using the more accurate malloc_usable_size call. (which is possibly slower than good_size, but almost certainly faster than reallocating).

Note: This also ties in with https://github.com/rust-lang/wg-allocators/issues/74, but they're useful independently, and not particularly useful together — the "together" functionality is provided by the current API, as the caller should just use one of the functions that provides both the size and pointer as a result.


* That is, it's legal iff the underlying allocator can handle the differences in Layout passed to its dealloc, which is something you know as the implementer of the AllocRef trait.

** For some allocator implementations this is meaningfully better (good_size might just round up), and only different in rare cases (for example, for huge allocation made directly to the OS where the allocator switches to a different size granularity).