4 releases
Uses old Rust 2015
0.1.9 | Jan 11, 2016 |
---|---|
0.1.7 | Oct 18, 2015 |
0.1.6 | Oct 16, 2015 |
0.1.5 | Oct 16, 2015 |
#335 in Memory management
39KB
807 lines
Allocators
Documentation
This crate provides a different memory allocators, as well as an
Allocator
trait for creating other custom allocators. A main goal of allocators is composability. For this reason, it also provides some composable primitives to be used as building blocks for chained allocators. This crate leans heavily on unsafe/unstable code at the moment, and should be considered very experimental.
Why?
For Rust to fulfill its description as a systems programming language, users need to have more fine-grained control over the way memory is allocated in their programs. This crate is a proof-of-concept that these mechanisms can be implemented in Rust and provide a safe interface to their users.
Allocator Traits
Allocator
This is the core trait for allocators to implement. All a type has to do is implement two unsafe functions: allocate_raw
and deallocate_raw
. This will likely require reallocate_raw
in the future.
BlockOwner
Allocators that implement this can say definitively whether they own a block.
Allocator Types
Scoped Allocator
This is useful for reusing a block of memory for temporary allocations in a tight loop. Scopes can be nested and values allocated in a scope cannot be moved outside it.
#![feature(placement_in_syntax)]
use allocators::{Allocator, Scoped};
#[derive(Debug)]
struct Bomb(u8);
impl Drop for Bomb {
fn drop(&mut self) {
println!("Boom! {}", self.0);
}
}
// new scoped allocator with a kilobyte of memory.
let alloc = Scoped::new(1024).unwrap();
alloc.scope(|inner| {
let mut bombs = Vec::new();
// allocate_val makes the value on the stack first.
for i in 0..100 { bombs.push(inner.allocate(Bomb(i)).unwrap())}
// watch the bombs go off!
});
// Allocators also have placement-in syntax.
let my_int = in alloc.make_place().unwrap() { 23 };
println!("My int: {}", *my_int);
Free List Allocator
This allocator maintains a list of free blocks of a given size.
use allocators::{Allocator, FreeList};
// create a FreeList allocator with 64 blocks of 1024 bytes.
let alloc = FreeList::new(1024, 64).unwrap();
for _ in 0..10 {
// allocate every block
let mut v = Vec::new();
for i in 0u8..64 {
v.push(alloc.allocate([i; 1024]).unwrap());
}
// no more blocks :(.
assert!(alloc.allocate([0; 1024]).is_err());
// all the blocks get pushed back onto the freelist at the end here,
// memory gets reused efficiently in the next iteration.
}
This allocator can yield very good performance for situations like the above, where each block's space is being fully used.
Composable Primitives
These are very underdeveloped at the moment, and lack a fluent API as well. They are definitely a back-burner feature at the moment, since the idea of composable allocators hasn't really proved its value yet.
Null Allocator
This will probably get a new name since "Null" has some misleading connotations.
It fails to allocate any request made to it, and panics when deallocated with.
Fallback Allocator
This composes two BlockOwners
: a main allocator and a fallback. If the main allocator fails to allocate, it turns to the fallback.
Proxy Allocator
This wraps any allocator and something which implements the ProxyLogger
trait, which provides functions to log allocation, deallocation, and reallocation in any arbitrary way. It has practical applications in debug builds for measuring how an allocator is being utilized.