-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Allocator traits and std::heap #32838
Comments
I unfortunately wasn't paying close enough attention to mention this in the RFC discussion, but I think that
Note that these can be added backwards-compatibly next to For consistency, |
Additionally, I think that the default implementations of This makes it easier to produce a high-performance implementation of |
…sakis `#[may_dangle]` attribute `#[may_dangle]` attribute Second step of rust-lang#34761. Last big hurdle before we can work in earnest towards Allocator integration (rust-lang#32838) Note: I am not clear if this is *also* a syntax-breaking change that needs to be part of a breaking-batch.
…sakis `#[may_dangle]` attribute `#[may_dangle]` attribute Second step of rust-lang#34761. Last big hurdle before we can work in earnest towards Allocator integration (rust-lang#32838) Note: I am not clear if this is *also* a syntax-breaking change that needs to be part of a breaking-batch.
Another issue: The doc for To me this implies that it must check that the alignment of the given address matches any constraint implied by However, I don't think the spec for the underlying
So, should the implementation of |
@gereeter you make good points; I will add them to the check list I am accumulating in the issue description. |
(at this point I am waiting for |
I'm new to Rust, so forgive me if this has been discussed elsewhere. Is there any thought on how to support object-specific allocators? Some allocators such as slab allocators and magazine allocators are bound to a particular type, and do the work of constructing new objects, caching constructed objects which have been "freed" (rather than actually dropping them), returning already-constructed cached objects, and dropping objects before freeing the underlying memory to an underlying allocator when required. Currently, this proposal doesn't include anything along the lines of Where would an object allocator type or trait fit into this proposal? Would it be left for a future RFC? Something else? |
I don't think this has been discussed yet. You could write your own Future work would be modifying collections to use your trait for their nodes, instead of plain ole' (generic) allocators directly. |
I guess this has happened? |
@Ericson2314 Yeah, writing my own is definitely an option for experimental purposes, but I think there'd be much more benefit to it being standardized in terms of interoperability (for example, I plan on also implementing a slab allocator, but it would be nice if a third-party user of my code could use somebody else's slab allocator with my magazine caching layer). My question is simply whether an |
Yes, it would be another RFC.
that depends on the scope of the RFC itself, which is decided by the person who writes it, and then feedback is given by everyone. But really, as this is a tracking issue for this already-accepted RFC, thinking about extensions and design changes isn't really for this thread; you should open a new one over on the RFCs repo. |
@joshlf Ah, I thought @steveklabnik yeah now discussion would be better elsewhere. But @joshlf was also raising the issue lest it expose a hitherto unforeseen flaw in the accepted but unimplemented API design. In that sense it matches the earlier posts in this thread. |
@Ericson2314 Yeah, I thought that was what you meant. I think we're on the same page :) @steveklabnik Sounds good; I'll poke around with my own implementation and submit an RFC if it ends up seeming like a good idea. |
@joshlf I don't any reason why custom allocators would go into the compiler or standard library. Once this RFC lands, you could easily publish your own crate that does an arbitrary sort of allocation (even a fully-fledged allocator like jemalloc could be custom-implemented!). |
@alexreg This isn't about a particular custom allocator, but rather a trait that specifies the type of all allocators which are parametric on a particular type. So just like RFC 1398 defines a trait ( |
@alexreg See my early point about using standard library collections with custom object-specific allocators. |
Sure, but I’m not sure that would belong in the standard library. Could easily go into another crate, with no loss of functionality or usability.
… On 4 Jan 2017, at 21:59, Joshua Liebow-Feeser ***@***.***> wrote:
@alexreg <https://github.com/alexreg> This isn't about a particular custom allocator, but rather a trait that specifies the type of all allocators which are parametric on a particular type. So just like RFC 1398 defines a trait (Allocator) that is the type of any low-level allocator, I'm asking about a trait (ObjectAllocator<T>) that is the type of any allocator which can allocate/deallocate and construct/drop objects of type T.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#32838 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAEF3IhyyPhFgu1EGHr_GM_Evsr0SRzIks5rPBZGgaJpZM4IDYUN>.
|
I think you’d want to use standard-library collections (any heap-allocated value) with an *arbitrary* custom allocator; i.e. not limited to object-specific ones.
… On 4 Jan 2017, at 22:01, John Ericson ***@***.***> wrote:
@alexreg <https://github.com/alexreg> See my early point about using standard library collections with custom object-specific allocators.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#32838 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAEF3CrjYIXqcv8Aqvb4VTyPcajJozICks5rPBbOgaJpZM4IDYUN>.
|
Yes but you probably want some standard library functionality to rely on it (such as what @Ericson2314 suggested).
Ideally you'd want both - to accept either type of allocator. There are very significant benefits to using object-specific caching; for example, both slab allocation and magazine caching give very significant performance benefits - take a look at the papers I linked to above if you're curious. |
But the object allocator trait could simply be a subtrait of the general allocator trait. It’s as simple as that, as far as I’m concerned. Sure, certain types of allocators can be more efficient than general-purpose allocators, but neither the compiler nor the standard really need to (or indeed should) know about this.
… On 4 Jan 2017, at 22:13, Joshua Liebow-Feeser ***@***.***> wrote:
Sure, but I’m not sure that would belong in the standard library. Could easily go into another crate, with no loss of functionality or usability.
Yes but you probably want some standard library functionality to rely on it (such as what @Ericson2314 <https://github.com/Ericson2314> suggested).
I think you’d want to use standard-library collections (any heap-allocated value) with an arbitrary custom allocator; i.e. not limited to object-specific ones.
Ideally you'd want both - to accept either type of allocator. There are very significant benefits to using object-specific caching; for example, both slab allocation and magazine caching give very significant performance benefits - take a look at the papers I linked to above if you're curious.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#32838 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAEF3L9F9r_0T5evOtt7Es92vw6gBxR9ks5rPBl9gaJpZM4IDYUN>.
|
Ah, so the problem is that the semantics are different.
This is not compatible with |
That makes sense; I forgot size was passed to dealloc.
…On Tue, Jun 27, 2023, 8:40 PM Amanieu ***@***.***> wrote:
The wrapper would return NonNull::dangling for all zero-sized allocations
and ignore all zero-sized deallocations. Non-zero allocations are passed on
to the underlying allocator.
—
Reply to this email directly, view it on GitHub
<#32838 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAA2LH4DZ5YKLOAMLJVIJGLXNOKR5ANCNFSM4CANQUGQ>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
This is true, but you end up with code performing the same checks many times. For example, both sides of the interface end up checking here if your underlying allocator doesn't handle zero-sized allocations well, and most (or at least very many) designs for allocators do not, in my experience. Given that forbidding it is consistent with the earlier design (GlobalAlloc), I don't really see what we get by allowing it at this point when it clearly causes trouble both for users and implementers of the API. |
It can't possible cause trouble for users -- every user that is correct wrt the more restricted API (that is UB on zero-sized allocs) is also correct wrt the current API. |
Another reason to have the collection check for zero size: for e.g.
Correctness trouble is the worst kind of trouble, but not the only kind. Users also want performance, which the current API pessimizes. |
I also don't necessarily agree that the current API avoids correctness issues. We clearly had one in the stdlib code, which would have almost certainly been avoided if we made it clear that zero-sized allocations were forbidden (especially if we forbid it by making things take a NonZeroLayout or such). My experience is that the sort of optimizations that the stdlib does here are quite common, and not a special case. This is likely because of how long we've documented that zero-sized allocations are free and infallible (something which definitely won't be universally true if we delegate this to the underlying allocator). |
FWIW, if I read 56cbf2f correctly, then RawVec actually did this correctly when the
I agree that for data structures that have a special zero-size state themselves where they don't own any memory that must be given back to the allocator, the current API does not help at all. The question is, is that case common enough to justify hurting the alternative case where data structures would be completely fine with always owning some allocated memory, even when the size is 0 -- basically leaving it to allocators to make size 0 efficient, instead of having to implement that optimization for each data structure? Naively it seems much better to do this in the allocator once rather than in every single data structure, but clearly Here is a possible alternative to just making That would make the buggy What I don't know is whether this API is something allocators can reasonably implement. @thomcc what do you think? |
How would you tell apart |
|
|
I'm not sure how it's const-compatible unless the allocator is a const trait/impl/something (or at least allocation is const), which doesn't seem likely to be the case most of the time (since usually allocation requires a number of operations which are problematic for const). I have to think about the rest of your comment though. |
To be clear, I have no idea if this proposal makes sense. The part where the allocator, in |
Oh, I see. I misinterpreted your proposal then. I think the problem here is that now |
So sounds like allocators would basically be forced to not have an actual allocation for size 0, so that they can make 'deallocate' always a NOP when the size is 0.
|
Yeah. We'd have to document that a zero sized allocation needs to be equivalent to dangling (or at least some kind of no-op), which seems a bit odd to me, but it would work. Basically, instead of telling users of Allocator that they cant' give allocators a zero-sized layout and should allocate a zero-sized layout in a certain way, we're saying they must behave a specific manner when given zero-sized layouts. The downside here is that the checks couldn't always be removed in cases where Allocator is a trait object. It also plausibly adds a branch into the allocation path that could otherwise be avoided. It also is a bit error-prone if not documented properly, as I go over in #32838 (comment) (although I've softened on this proposal since the issues I hit could possibly be addressed with documentation). This would make the "handle zero-sized layout by rounding up" approach suggested elsewhere in this thread invalid though (but I don't see a way to keep it without many other downsides). |
I wrote a blog post announcing that I'm intending on working on the Largely speaking my feeling that Anyway, all of this is tricky because Footnotes
|
I'm sorry but isn't both "new_in" and "with_capacity_in" have very minor mistakes in source documentation in example section? Or am I missing something? |
@wallgeek No, you're right. They don't exemplify the APIs at all. It should be fixed! |
I've checked all
The rest is okay. |
Currently in std: const B: usize = 6; The struct-level docs explain this and compare a B-tree with a binary tree that would have individual allocations for each item: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
It’s probably not relevant for the docs of a constructor to talk about the "choice" of B, since that choice is compile-time constant in the current implementation. (As opposed to something users could influence like |
Add missing try_new_uninit_slice_in and try_new_zeroed_slice_in The methods for fallible slice allocation in a given allocator were missing from `Box`, which was an oversight according to rust-lang/wg-allocators#130 This PR adds them as `try_new_uninit_slice_in` and `try_new_zeroed_slice_in`. I simply copy-pasted the implementations of `try_new_uninit_slice` and `try_new_zeroed_slice` and adusted doc comment, typings, and the allocator it uses internally. Also adds missing punctuation to the doc comments of `try_new_uninit_slice` and `try_new_zeroed_slice`. Related issue is rust-lang#32838 (Allocator traits and std::heap) *I think*. Also relevant is rust-lang#63291, but I did not add the corresponding `#[unstable]` proc macro, since `try_new_uninit_slice` and `try_new_zeroed_slice` are also not annotated with it.
Rollup merge of rust-lang#127415 - AljoschaMeyer:master, r=dtolnay Add missing try_new_uninit_slice_in and try_new_zeroed_slice_in The methods for fallible slice allocation in a given allocator were missing from `Box`, which was an oversight according to rust-lang/wg-allocators#130 This PR adds them as `try_new_uninit_slice_in` and `try_new_zeroed_slice_in`. I simply copy-pasted the implementations of `try_new_uninit_slice` and `try_new_zeroed_slice` and adusted doc comment, typings, and the allocator it uses internally. Also adds missing punctuation to the doc comments of `try_new_uninit_slice` and `try_new_zeroed_slice`. Related issue is rust-lang#32838 (Allocator traits and std::heap) *I think*. Also relevant is rust-lang#63291, but I did not add the corresponding `#[unstable]` proc macro, since `try_new_uninit_slice` and `try_new_zeroed_slice` are also not annotated with it.
With allocator_api, we now have safe memory allocation methods like try_new() for types like Box and Arc, and some collections (Vec, HashMap) support try_reserve as a workaround. However, collections like BTreeMap lack equivalent allocation-checked methods for operations like insert. To build a more consistent API, could we introduce allocation-checked variants across std::collections::* with a clear prefix like checked_, or safe_, etc.. (e.g., safe_insert, checked_push)? These methods would return Result<T, AllocError> on allocation failure, streamlining safe memory allocation handling. |
📢 This feature has a dedicated working group, please direct comments and concerns to the working group's repo.
The remainder of this post is no longer an accurate summary of the current state; see that dedicated working group instead.
Old content
Original Post:
FCP proposal: #32838 (comment)
FCP checkboxes: #32838 (comment)
Tracking issue for rust-lang/rfcs#1398 and the
std::heap
module.struct Layout
,trait Allocator
, and default implementations inalloc
crate (Allocator integration #42313)alloc
crate, butLayout
/Allocator
could be inlibcore
...) (Allocator integration #42313)Layout
for overflow errors, (potentially switching to overflowing_add and overflowing_mul as necessary).realloc_in_place
should be replaced withgrow_in_place
andshrink_in_place
(comment) (Allocator integration #42313)fn dealloc
. (See discussion on allocator rfc and global allocator rfc and traitAlloc
PR.)align
that you allocate with? Concerns have been raised that allocators like jemalloc don't require this, and it's difficult to envision an allocator that does require this. (more discussion). @ruuda and @rkruppe look like they've got the most thoughts so far on this.AllocErr
beError
instead? (comment)usable_size
business we may wish to allow, for example, that you if you allocate with(size, align)
you must deallocate with a size somewhere in the range ofsize...usable_size(size, align)
. It appears that jemalloc is totally ok with this (doesn't require you to deallocate with a precisesize
you allocate with) and this would also allowVec
to naturally take advantage of the excess capacity jemalloc gives it when it does an allocation. (although actually doing this is also somewhat orthogonal to this decision, we're just empoweringVec
). So far @gankro has most of the thoughts on this. (@alexcrichton believes this was settled in Allocator integration #42313 due to the definition of "fits")alloc_system
is buggy on huge alignments (e.g. an align of1 << 32
) if huge requested align, alloc_system heap::allocate on OS X returns unaligned values #30170 Add precondition toLayout
that thealign
fit in a u32. #43217Layout
provide afn stride(&self)
method? (See also Separate size and stride for types rfcs#1397, Collapse trailing padding #17027 )Allocator::owns
as a method? Alloc: Add owns method #44302State of
std::heap
after #42313:The text was updated successfully, but these errors were encountered: