Skip to content

Implement allocators, integrate with box, smart pointers, containers #12038

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

Closed
brson opened this issue Feb 5, 2014 · 19 comments
Closed

Implement allocators, integrate with box, smart pointers, containers #12038

brson opened this issue Feb 5, 2014 · 19 comments
Labels
A-allocators Area: Custom and system allocators P-medium Medium priority

Comments

@huonw
Copy link
Member

huonw commented Feb 5, 2014

#4252 is required for allocators (to be able to call .free in Drop impls).

@pnkfelix
Copy link
Member

pnkfelix commented Feb 6, 2014

Assigning 1.0 (at least to put a spec in, if nothing else, though we probably should have an implementation as well).

Depending on how allocators integrate with box as well as std libraries, could be P-backcompat-libs (but a case could be made for lang)...

@pnkfelix pnkfelix added this to the 1.0 milestone Feb 6, 2014
@brson
Copy link
Contributor Author

brson commented Feb 6, 2014

We at least need to have high-confidence that we can do it post-1.0.

@thestinger
Copy link
Contributor

If it's done with default parameters, it would be backwards compatible.

Vec<T> -> Vec<T, A = Heap>
Rc<T> -> Rc<T, A = Heap>
HashMap<K, V> -> Vec<K, V, A = Heap>

Functions like new would then be defined only for A = Heap and a with_alloc function taking an allocator parameter would be available for passing a custom allocator instance:

// this becomes a TreeSet<int, Heap> and will still print in errors as TreeSet<int>
let mut xs = TreeSet::new();
xs.insert(5);
channel.send(xs);
// this is a TreeSet<int, CacheAlignedAllocator>
let mut xs = TreeSet::with_alloc(CACHE_ALIGNED_ALLOCATOR);
xs.insert(5);
channel.send(xs);

@nikomatsakis
Copy link
Contributor

cc me

@cgaebel
Copy link
Contributor

cgaebel commented Mar 1, 2014

I was at Bloomberg last year, and I found https://github.com/bloomberg/bde/wiki/BDE-Allocator-Model to be extremely powerful, and potentially something to model Rust's allocators after.

Pros: Where your memory comes from is very explicit. It makes it very easy to do things such as hierarchical allocators. For example, having a bump allocator allocate its memory out of an arena of bump allocators which allocates its memory out of the stack. Writing code that only uses stack in inner-loops becomes possible, and I've seen very little code like it outside of that company.

Cons: Where your memory comes from is very explicit. Basically every struct that can allocate memory gains a new Allocator pointer. It works pretty well in large, homogeneous C++ codebases, but maybe rust can do something smarter here. I don't like having to add that field myself, every time, but I also wouldn't like for my structs to gain implicit fields. There is a relatively large design space here to explore.

@huonw
Copy link
Member

huonw commented Mar 1, 2014

Basically every struct that can allocate memory gains a new Allocator pointer

The precise details of this isn't quite true. Every struct that can allocate memory would gain a new field like (for illustration purposes):

struct Foo<A: Allocator> {
    // ...

    alloc: A
}

i.e. not required to be a pointer. Also, Rust has well-defined zero-sized types, so one can substitute in something like struct CAlloc;that just calls malloc/free, and struct Jemalloc; calling jemalloc's routines. If someone does have an allocator that needs to be behind a pointer (for size or for shared data), they're free to do that as part of the implementation details of it.

(Just glancing at the BDE API now, it seems like it would be preferable for Rust to expose an API like jemalloc's experimental API, where e.g. malloc returns the true usable size of the allocations, and you can request some optional extra size for reallocs, etc.)


FWIW, #4252 is now closed, so it's probably possible to start experimenting with this seriously.

@cgaebel
Copy link
Contributor

cgaebel commented Mar 1, 2014

The way BDE does it, is that every struct/class which allocates gets an Allocator*. When you create one of these objects, you pass it one. You could also pass null (which the parameter defaults to), in which case it uses the global allocator. But either way, it doesn't store the allocator itself - only a pointer to it. This might be hard to replicate in rust (with memory safety and all), but using a reference counted pointer might be appropriate for this.

@huonw
Copy link
Member

huonw commented Mar 1, 2014

If some allocator needs shared state, the Allocator struct can use something like

struct MyCoolAllocator {
     priv inner: Rc<MyCoolSharedState>
}

impl Allocator for MyCoolAllocator { ... }

We definitely don't need to force every struct taking an allocator to be a word larger (since we have zero-sized types, unlike C++), or force the entirety of each allocators to be behind a pointer.

@cgaebel
Copy link
Contributor

cgaebel commented Mar 1, 2014

The allocators are designed as honest to goodness classes in BDE, with pure virtual allocate() and deallocate() methods. I've seen traditional C++ template-based allocators in use and BDE allocators in practice, and I much preferred BDEs. The semantics were extremely clean, and the vtable dispatches were entirely worth it.

@huonw
Copy link
Member

huonw commented Mar 1, 2014

Why not have

impl Allocator for ~Allocator { ... }
// etc

(would be just impl Allocator for Allocator { ... } with DST.)

so one can choose between extra words & virtual calls, and static dispatch (and no extra words) seamlessly? (I guess this might not be quite so clean.)

@cgaebel
Copy link
Contributor

cgaebel commented Mar 1, 2014

Yeah. At this point we're rehashing a lot of arguments that the guys behind BDE hashed out over years with the C++ standards committee. It's... a noisy one.

I'm just putting the idea out there for now for when this gets looked at, as a possible inspiration for design.

@nikomatsakis
Copy link
Contributor

I've taken the liberty of updating the head comment of this issue. I'm going to try and keep it up to date with interesting tidbits and (eventually) a link to a concrete rfc.

@cgaebel
Copy link
Contributor

cgaebel commented Mar 1, 2014

One more subtlety that will very likely come up during implementation: copy and move semantics. A copy will have to specify a new allocator to allocate out of, and there will likely have to be two versions of move: one which just moves a pointer, and the other which will move memory between two allocation zones.

@reem
Copy link
Contributor

reem commented Sep 15, 2014

Triage: There seems to be active work on this.

@thestinger thestinger added the A-allocators Area: Custom and system allocators label Sep 16, 2014
@brson
Copy link
Contributor Author

brson commented Oct 1, 2014

Nominating for removal. We've recently said that allocators are not 1.0.

@pnkfelix
Copy link
Member

pnkfelix commented Oct 2, 2014

(P-high, not 1.0; removing nomination tag; should migrate to rfc repo issue.)

@pnkfelix pnkfelix removed this from the 1.0 milestone Oct 2, 2014
@pnkfelix pnkfelix added P-medium Medium priority and removed P-backcompat-libs labels Oct 2, 2014
@pnkfelix
Copy link
Member

(filed on RFC repo issue rust-lang/rfcs#538 )

@steveklabnik
Copy link
Member

Closing in favor of the RFC issue.

flip1995 pushed a commit to flip1995/rust that referenced this issue Dec 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-allocators Area: Custom and system allocators P-medium Medium priority
Projects
None yet
Development

No branches or pull requests

8 participants