bumpalo

repository·main·Indexed 25 days ago

https://github.com/fitzgen/bumpalo

A fast bump allocation arena for Rust, designed for phase-oriented allocations where objects are allocated quickly and deallocated en masse. It provides a `Bump` arena for efficient memory management, optional support for `std::alloc::Allocator` (via nightly or `allocator-api2`), and arena-allocated collections. Features include `serde` support, a `boxed` module to ensure `Drop` is called for specific objects, and a `CollectIn` trait for collecting iterators into arena-allocated collections.

Tokens
12.2K
Snippets
44
Records
73
Agent score
81%

What's inside bumpalo

  1. Overview of benchmark suites

    main

    The bumpalo benchmark directory contains two distinct suites:

    1. allocator_api.rs: Measures the performance of bump allocators using the generic std::alloc::Allocator API. These benchmarks focus on raw allocation, growing, and shrinking operations.
    2. benches.rs: Contains miscellaneous Bumpalo-specific benchmarks, including performance when using collections like Vec<T> with a bump allocator.

    Note: These are micro-benchmarks. While they show the relative performance of bumpalo against blink-alloc and std::alloc::System, application-level performance may vary based on whether the application is actually bottlenecked by allocation.

  2. How bump allocation works in bumpalo

    main

    Bump allocation is a high-performance allocation strategy where a large chunk of memory is pre-allocated, and a pointer is maintained within that chunk. When an object is requested, the allocator checks if there is enough capacity, then simply increments the pointer by the object's size.

    Key Characteristics:

    • Fast: Allocation is extremely efficient.
    • Limited Deallocation: There is no way to deallocate individual objects or reclaim specific memory regions.
    • Phase-Oriented: It is best suited for 'phase-oriented' allocations, where a group of objects are all used during a specific program phase and can then be deallocated together as a single group.
    • Automatic Growth: If a memory chunk becomes full, bumpalo allocates a new chunk from the global allocator and continues bump allocating into the new chunk.
  3. Deallocating memory in a Bump arena

    main

    To deallocate all objects in a Bump arena at once, you can reset the bump pointer back to the start of the arena's memory chunk. This mass deallocation is extremely fast.

    Warning: Resetting the arena does not invoke the Drop implementation for any objects allocated within it. If your objects require cleanup (e.g., closing file handles or releasing other resources), you must use bumpalo::boxed::Box<T> to ensure Drop is called when the box goes out of scope.

  4. Update benchmark result tables

    main

    If you want to update the benchmark tables in the documentation, use cargo-criterion and criterion-table. Navigate to the bumpalo/benches/ directory and run the following sequence:

    $ cd bumpalo/benches/
    $ cargo +nightly criterion --features bench_allocator_api \
        --bench allocator_api \
        --message-format=json \
        > results.json
    $ criterion-table < results.json > README.md
  5. Use the Allocator API on Stable Rust

    main
    If you are on stable Rust but want to use the Allocator API pattern, enable the "allocator-api2" Cargo feature. bumpalo will then use the allocator-api2 crate to provide an implementation of the Allocator trait, making bumpalo::Bump compatible with any collection that is generic over allocator_api2::Allocator.
  6. Reproduce allocator-api benchmarks

    main

    To reproduce the std::alloc::Allocator-based benchmarks, you must use a nightly Rust toolchain because the Allocator trait is currently unstable. You also need to enable the allocator_api cargo feature for bumpalo.

    Run the following command:

    $ cargo +nightly bench --bench allocator_api --features allocator_api
  7. Use the nightly Rust allocator_api with bumpalo

    main

    To use bumpalo::Bump with standard library collections via the unstable nightly allocator_api, follow these steps:

    1. Enable the "allocator_api" feature in Cargo.toml.
    2. Enable the #![feature(allocator_api)] nightly Rust feature in your crate root.

    Note: The allocator_api feature in bumpalo is considered unstable and does not follow semver conventions.

    [dependencies]
    bumpalo = { version = "3", features = ["allocator_api"] }
    #![feature(allocator_api)]
    
    use bumpalo::Bump;
    
    let bump = Bump::new();
    // Create a `Vec` whose elements are allocated within the bump arena.
    let mut v = Vec::new_in(&bump);
    v.push(0);
  8. Use collection types that allocate in a Bump arena

    main

    The bumpalo::collections module provides collection types designed to allocate memory within a Bump arena. This allows for fast allocation and bulk deallocation of entire collections when the arena is reset.

    Available collection types include:

    • Vec
    • String

    Additionally, the module provides traits for collecting items directly into these arena-allocated collections, such as CollectIn and FromIteratorIn.

  9. What is RawVec and when to use it

    main

    A RawVec<'a, T> is a low-level utility for managing a heap-allocated buffer of memory. It is designed for building custom data structures like Vec or VecDeque.

    Key Characteristics:

    • Memory Management: It manages the allocation, reallocation, and deallocation of a buffer but does not inspect or drop the contents stored within the buffer. The user is responsible for handling the lifecycle (dropping) of the elements T.
    • Zero-Sized Types (ZSTs): For ZSTs, cap() always returns usize::MAX. This allows capacity-growing logic to function without special-casing ZSTs in consumer code.
    • Safety Guards: It prevents overflows in capacity computations, guards against 32-bit systems allocating more than isize::MAX bytes, and aborts on Out-Of-Memory (OOM).
    • Deallocation: When dropped, it frees the underlying memory but does not call drop() on the elements.
  10. Enable Serde support for bumpalo

    main

    By enabling the "serde" feature flag, you can transparently serialize Vecs, Strings, and boxed values allocated within a Bump arena using serde.

    [dependencies]
    bumpalo = { version = "3.18", features = ["collections", "boxed", "serde"] }
  11. Use the `Emplace` API for optimized allocation

    main

    The Emplace API separates allocation from initialization. This allows the compiler to construct a value directly inside the allocated space, avoiding an extra stack-to-arena move. This is particularly useful for complex types or when using fallible initialization.

    Example of direct construction:

    let x = bump.emplace().write(42);

    Example of fallible initialization with ? propagation:

    let mut place = bump.emplace_slice_with_capacity(10);
    for result in my_fallible_collection() {
        let item = result?;
        place.push(item);
    }
    let slice = place.into_slice();
    use bumpalo::Bump;
    
    let bump = Bump::new();
    
    let x = bump
        // Pre-allocate space for the value.
        .emplace()
        // Write the value into the pre-allocated space.
        .write(42);
    
    assert_eq!(*x, 42);