pub struct SyncBlinkAlloc<A: Allocator = Global> { /* private fields */ }
Expand description

Multi-threaded blink allocator.

Blink-allocator is arena-based allocator that allocates memory in growing chunks and serve allocations from them. When chunk is exhausted a new larger chunk is allocated.

Deallocation is no-op. BlinkAllocator can be reset to free all chunks except the last one, that will be reused.

Blink allocator aims to allocate a chunk large enough to serve all allocations between resets.

A shared and mutable reference to the SyncBlinkAlloc implement Allocator trait. When “nightly” feature is enabled, Allocator trait is core::alloc::Allocator. Otherwise it is duplicated trait defined in allocator-api2.

Resetting blink allocator requires mutable borrow, so it is not possible to do while shared borrow is alive. That matches requirement of Allocator trait - while Allocator instance (a shared reference to BlinkAlloc) or any of its clones are alive, allocated memory must be valid.

This version of blink-allocator is multi-threaded. It can be used from multiple threads concurrently to allocate memory. As mutable borrow is required to reset the allocator, it is not possible to do when shared. Internally it uses RwLock and AtomicUsize for synchronized interior mutability. RwLock is only write-locked when new chunk must be allocated. The arena allocation is performed using lock-free algorithm.

Still it is slower than single-threaded version BlinkAlloc.

For best of both worlds LocalBlinkAlloc can be created from this allocator. LocalBlinkAlloc will allocate chunks from this allocator, but is single-threaded by itself.

Example


let mut blink = SyncBlinkAlloc::new();
let layout = std::alloc::Layout::new::<[u32; 8]>();
let ptr = blink.allocate(layout).unwrap();
let ptr = NonNull::new(ptr.as_ptr() as *mut u8).unwrap(); // Method for this is unstable.

unsafe {
    std::ptr::write(ptr.as_ptr().cast(), [1, 2, 3, 4, 5, 6, 7, 8]);
}

blink.reset();

Example that uses nightly’s allocator_api

let mut blink = SyncBlinkAlloc::new();
let mut vec = Vec::new_in(&blink);
vec.push(1);
vec.extend(1..3);
vec.extend(3..10);
drop(vec);
blink.reset();

Implementations§

source§

impl SyncBlinkAlloc<Global>

source

pub const fn new() -> Self

Creates new blink allocator that uses global allocator to allocate memory chunks.

See SyncBlinkAlloc::new_in for using custom allocator.

source§

impl<A> SyncBlinkAlloc<A>where A: Allocator,

source

pub const fn new_in(allocator: A) -> Self

Creates new blink allocator that uses provided allocator to allocate memory chunks.

See SyncBlinkAlloc::new for using global allocator.

source

pub const fn inner(&self) -> &A

Returns reference to the underlying allocator used by this blink allocator.

source

pub const fn with_chunk_size_in(chunk_size: usize, allocator: A) -> Self

Creates new blink allocator that uses global allocator to allocate memory chunks. With this method you can specify initial chunk size.

See SyncBlinkAlloc::new_in for using custom allocator.

source

pub fn local(&self) -> LocalBlinkAlloc<'_, A>

Creates a new thread-local blink allocator proxy that borrows from this multi-threaded allocator.

The local proxy allocator works faster and allows more consistent memory reuse. It can be recreated without resetting the multi-threaded allocator, allowing SyncBlinkAlloc to be warm-up and serve all allocations from a single chunk without ever blocking.

Best works for fork-join style of parallelism. Create a local allocator for each thread/task. Reset after all threads/tasks are finished.

Examples
let mut blink = SyncBlinkAlloc::new();
for _ in 0..3 {
    for i in 0..16 {
        std::thread::scope(|_| {
            let blink = blink.local();
            let mut vec = Vec::new_in(&blink);
            vec.push(i);
            for j in i*2..i*30 {
                vec.push(j); // Proxy will allocate enough memory to grow vec without reallocating on 2nd iteration and later.
            }
        });
    }
    blink.reset();
}
source

pub fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

Allocates memory with specified layout from this allocator. If needed it will allocate new chunk using underlying allocator. If chunk allocation fails, it will return Err.

source

pub unsafe fn resize( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout ) -> Result<NonNull<[u8]>, AllocError>

Resizes memory allocation. Potentially happens in-place.

Safety

ptr must be a pointer previously returned by allocate. old_size must be in range layout.size()..=slice.len() where layout is the layout used in the call to allocate. and slice is the slice pointer returned by allocate.

On success, the old pointer is invalidated and the new pointer is returned. On error old allocation is still valid.

source

pub unsafe fn deallocate(&self, ptr: NonNull<u8>, size: usize)

Deallocates memory previously allocated from this allocator.

This call may not actually free memory. All memory is guaranteed to be freed on reset call.

Safety

ptr must be a pointer previously returned by allocate. size must be in range layout.size()..=slice.len() where layout is the layout used in the call to allocate. and slice is the slice pointer returned by allocate.

source

pub fn reset(&mut self)

Resets this allocator, deallocating all chunks except the last one. Last chunk will be reused. With steady memory usage after few iterations one chunk should be sufficient for all allocations between resets.

source

pub fn reset_final(&mut self)

Resets this allocator, deallocating all chunks.

source

pub unsafe fn reset_unchecked(&self)

Resets this allocator, deallocating all chunks except the last one. Last chunk will be reused. With steady memory usage after few iterations one chunk should be sufficient for all allocations between resets.

Safety

Blink-allocators guarantee that memory can be used while shared borrow to the allocator is held, preventing safe fn reset call.

With this method it becomes caller responsibility to ensure that allocated memory won’t be used after reset.

source

pub fn into_inner(self) -> A

Unwrap this allocator, returning the underlying allocator. Leaks allocated chunks.

To deallocate all chunks call reset_final first.

The second returned value will use global allocator, so use with care if this method is used inside global allocator.

source

pub fn update_max_local_alloc(&self, max_local_alloc: usize)

Update maximum local allocation size. Can be used by thread-local blink-allocators that use this shared blink-allocator.

Trait Implementations§

source§

impl<A> Allocator for &mut SyncBlinkAlloc<A>where A: Allocator,

source§

fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Attempts to allocate a block of memory. Read more
source§

fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Behaves like allocate, but also ensures that the returned memory is zero-initialized. Read more
source§

unsafe fn shrink( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout ) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Attempts to shrink the memory block. Read more
source§

unsafe fn grow( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout ) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Attempts to extend the memory block. Read more
source§

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout)

🔬This is a nightly-only experimental API. (allocator_api)
Deallocates the memory referenced by ptr. Read more
source§

unsafe fn grow_zeroed( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout ) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Behaves like grow, but also ensures that the new contents are set to zero before being returned. Read more
source§

fn by_ref(&self) -> &Selfwhere Self: Sized,

🔬This is a nightly-only experimental API. (allocator_api)
Creates a “by reference” adapter for this instance of Allocator. Read more
source§

impl<A> Allocator for SyncBlinkAlloc<A>where A: Allocator,

source§

fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Attempts to allocate a block of memory. Read more
source§

unsafe fn shrink( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout ) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Attempts to shrink the memory block. Read more
source§

unsafe fn grow( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout ) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Attempts to extend the memory block. Read more
source§

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout)

🔬This is a nightly-only experimental API. (allocator_api)
Deallocates the memory referenced by ptr. Read more
source§

fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Behaves like allocate, but also ensures that the returned memory is zero-initialized. Read more
source§

unsafe fn grow_zeroed( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout ) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (allocator_api)
Behaves like grow, but also ensures that the new contents are set to zero before being returned. Read more
source§

fn by_ref(&self) -> &Selfwhere Self: Sized,

🔬This is a nightly-only experimental API. (allocator_api)
Creates a “by reference” adapter for this instance of Allocator. Read more
source§

impl<A> BlinkAllocator for SyncBlinkAlloc<A>where A: Allocator,

source§

fn reset(&mut self)

Resets allocator potentially invalidating all allocations made from this instance. This is no-op if allocator is Clone (typically shared reference to blink-allocator). Read more
source§

impl<A> Default for SyncBlinkAlloc<A>where A: Allocator + Default,

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<A: Allocator> Drop for SyncBlinkAlloc<A>

source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl<A = Global> !RefUnwindSafe for SyncBlinkAlloc<A>

§

impl<A> Send for SyncBlinkAlloc<A>where A: Send,

§

impl<A> Sync for SyncBlinkAlloc<A>where A: Sync,

§

impl<A> Unpin for SyncBlinkAlloc<A>where A: Unpin,

§

impl<A> UnwindSafe for SyncBlinkAlloc<A>where A: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.