vecpool 0.1.0

Thread-local pool of reusable Vec buffers with cross-type reuse
Documentation
  • Coverage
  • 100%
    7 out of 7 items documented7 out of 7 items with examples
  • Size
  • Source code size: 44.74 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 936.23 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 7s Average build duration of successful builds.
  • all releases: 7s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • MichaelDuPlessis/vecpool
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • MichaelDuPlessis

vecpool

Crates.io Documentation License

A thread-local pool of reusable Vec buffers with cross-type reuse.

Why?

Allocating and deallocating Vecs in a loop is expensive. vecpool recycles the underlying heap buffers so subsequent allocations are free — just a pointer pop from a thread-local stack.

Unlike per-type pools, vecpool keys buffers by memory layout (size, align), so a buffer originally used for Vec<i32> can be reused for Vec<u32>, Vec<f32>, or any other 4-byte aligned type.

Usage

[dependencies]
vecpool = "0.1"
use vecpool::{get, with_capacity, PoolVec};

// Get a vec from the pool
let mut v = get::<u32>();
v.push(1);
v.push(2);
v.push(3);

// Use it exactly like a Vec<u32>
assert_eq!(v.len(), 3);
assert_eq!(&*v, &[1, 2, 3]);

// On drop, the buffer is returned to the pool
drop(v);

// Next request reuses the buffer — zero allocation cost
let v2 = get::<u32>();
assert!(v2.capacity() >= 3);

Features

  • Cross-type reuseVec<i32>Vec<u32>Vec<f32> share the same pool lane
  • Zero synchronization — one pool per thread, no mutexes or atomics
  • TransparentPoolVec<T> derefs to Vec<T>, all Vec methods work
  • Fast — power-of-two sizes use a direct array lookup (trailing_zeros), no hashing
  • Safe — all unsafe code is Miri-tested, no undefined behavior

API Overview

Function / Method Description
get::<T>() Get an empty pooled vec
with_capacity::<T>(n) Get a pooled vec with at least n capacity
PoolVec::new() Alias for get::<T>()
PoolVec::from(vec) Wrap an existing Vec<T> for pool return on drop
pool_vec.into_vec() Extract the inner Vec<T> (skips pool return)
.collect::<PoolVec<T>>() Collect an iterator into a pooled vec
pool_vec.into_iter() Iterate elements (buffer escapes the pool)
clear_pool() Free all pooled buffers on the current thread

How It Works

  1. Each thread has a local pool with "lanes" keyed by (size_of::<T>(), align_of::<T>())
  2. Each lane is a LIFO stack of raw buffers (pointer + byte capacity)
  3. get::<T>() pops a buffer from the matching lane and reconstructs a Vec<T> with len = 0
  4. On drop, PoolVec<T> clears elements, extracts the raw buffer, and pushes it back
  5. If no buffer is available, a fresh Vec is allocated normally

Fast Path

For types where size == align and size is a power of two ≤ 256 (covers all primitives, pointers, and most small types), the pool uses a fixed-size array indexed by size.trailing_zeros(). This avoids hashing entirely.

Thread Safety

PoolVec<T>: Send where T: Send. If a PoolVec is moved to another thread and dropped there, the buffer goes to that thread's pool. No synchronization is needed.

If the thread-local pool is being destroyed (thread exit), the buffer is freed normally instead of being pooled.

Benchmarks

Run benchmarks with:

cargo bench

The benchmark suite compares pooled vs unpooled allocation across different element sizes, pool depths, and usage patterns.

Minimum Supported Rust Version

Rust 2024 edition (1.85+).

License

MIT