Skip to main content

ScratchBank

Struct ScratchBank 

Source
pub struct ScratchBank<T, const N: usize>
where T: ScratchElement,
{ /* private fields */ }
Expand description

A fixed set of same-typed scratch pools for domain-specific temporary roles.

Transform crates commonly need several independent thread-local scratch buffers for one element type: e.g. Stockham data, PFA data, Rader padding, and Bluestein chirps. ScratchBank<T, N> keeps those roles in one const-generic provider-owned container while preserving the same zero-copy ScratchPool::with_scratch access contract for each slot.

Implementations§

Source§

impl<T, const N: usize> ScratchBank<T, N>
where T: ScratchElement,

Source

pub const fn new() -> ScratchBank<T, N>

Creates a bank of empty scratch pools.

Examples found in repository?
examples/book_scratch_pool.rs (line 68)
22fn main() {
23    // Construct a pool locally and use it directly (not thread_local! here
24    // to avoid the clippy::missing_const_for_thread_local false positive on
25    // 1.97.0 — see ATLAS-MNEMOSYNE-CI-1).  In production callers store the
26    // pool in thread_local! storage so it persists between calls.
27    let f64_pool: ScratchPool<f64> = ScratchPool::new();
28    let f32_pool: ScratchPool<f32> = ScratchPool::new();
29
30    // Simple single-level scratch use: compute a dot product via a temp buffer.
31    f64_pool.with_scratch(1024, |scratch| {
32        for (i, slot) in scratch.iter_mut().enumerate() {
33            *slot = i as f64;
34        }
35        let partial: f64 = scratch.iter().sum();
36        println!(
37            "sum of 0..1024 via scratch: {} (expected {})",
38            partial,
39            (0..1024usize).sum::<usize>() as f64,
40        );
41        assert_eq!(partial, (0..1024usize).sum::<usize>() as f64);
42    });
43
44    // Nested borrows: outer scratch holds the signal; inner holds a window.
45    f64_pool.with_scratch(256, |signal| {
46        for (i, s) in signal.iter_mut().enumerate() {
47            *s = (i as f64).sin();
48        }
49        f64_pool.with_scratch(16, |window| {
50            window.copy_from_slice(&signal[..16]);
51            let dp = dot_product(window, &signal[..16]);
52            println!("windowed dot-product: {dp:.6}");
53            assert!(dp.is_finite());
54        });
55    });
56
57    // F32 pool: same API, different element type.
58    f32_pool.with_scratch(64, |buf| {
59        buf.fill(1.0_f32);
60        let total: f32 = buf.iter().sum();
61        println!("f32 scratch sum: {total}");
62        assert_eq!(total, 64.0_f32);
63    });
64
65    // Banked scratch keeps multiple related roles in one const-generic group,
66    // which matches transform pipelines that need independent temporary views
67    // without falling back to the system allocator.
68    let bank: ScratchBank<f64, 2> = ScratchBank::new();
69    bank.with_scratch::<1, _>(32, |scratch| {
70        scratch.fill(3.5);
71        assert!(scratch.iter().all(|v| *v == 3.5));
72    });
73
74    // Bounded provisioning retains the working set while making geometric
75    // growth headroom reclaimable at a consumer-selected quiescent point.
76    let bounded_pool: ScratchPool<u32> = ScratchPool::new();
77    bounded_pool.with_scratch_bounded(1024, |_| {});
78    bounded_pool.with_scratch_bounded(1025, |_| {});
79    assert_eq!(bounded_pool.capacity(), 2048);
80    let retained = bounded_pool.release();
81    println!(
82        "bounded release: capacity={} -> retained={}",
83        2048, retained[0]
84    );
85    assert_eq!(retained[0], 1025);
86    bounded_pool.reset();
87    assert_eq!(bounded_pool.release()[0], 0);
88
89    println!("MAX_POOL_SLOTS = {MAX_POOL_SLOTS} (max concurrent nested borrows)");
90    println!("all scratch-pool assertions passed");
91}
Source

pub fn with_scratch<const INDEX: usize, R>( &self, n: usize, f: impl FnOnce(&mut [T]) -> R, ) -> R

Runs f with scratch from slot INDEX, sized to exactly n elements.

INDEX is a const generic so role selection is resolved at compile time at monomorphized call sites.

§Panics

Panics when INDEX >= N.

Examples found in repository?
examples/book_scratch_pool.rs (lines 69-72)
22fn main() {
23    // Construct a pool locally and use it directly (not thread_local! here
24    // to avoid the clippy::missing_const_for_thread_local false positive on
25    // 1.97.0 — see ATLAS-MNEMOSYNE-CI-1).  In production callers store the
26    // pool in thread_local! storage so it persists between calls.
27    let f64_pool: ScratchPool<f64> = ScratchPool::new();
28    let f32_pool: ScratchPool<f32> = ScratchPool::new();
29
30    // Simple single-level scratch use: compute a dot product via a temp buffer.
31    f64_pool.with_scratch(1024, |scratch| {
32        for (i, slot) in scratch.iter_mut().enumerate() {
33            *slot = i as f64;
34        }
35        let partial: f64 = scratch.iter().sum();
36        println!(
37            "sum of 0..1024 via scratch: {} (expected {})",
38            partial,
39            (0..1024usize).sum::<usize>() as f64,
40        );
41        assert_eq!(partial, (0..1024usize).sum::<usize>() as f64);
42    });
43
44    // Nested borrows: outer scratch holds the signal; inner holds a window.
45    f64_pool.with_scratch(256, |signal| {
46        for (i, s) in signal.iter_mut().enumerate() {
47            *s = (i as f64).sin();
48        }
49        f64_pool.with_scratch(16, |window| {
50            window.copy_from_slice(&signal[..16]);
51            let dp = dot_product(window, &signal[..16]);
52            println!("windowed dot-product: {dp:.6}");
53            assert!(dp.is_finite());
54        });
55    });
56
57    // F32 pool: same API, different element type.
58    f32_pool.with_scratch(64, |buf| {
59        buf.fill(1.0_f32);
60        let total: f32 = buf.iter().sum();
61        println!("f32 scratch sum: {total}");
62        assert_eq!(total, 64.0_f32);
63    });
64
65    // Banked scratch keeps multiple related roles in one const-generic group,
66    // which matches transform pipelines that need independent temporary views
67    // without falling back to the system allocator.
68    let bank: ScratchBank<f64, 2> = ScratchBank::new();
69    bank.with_scratch::<1, _>(32, |scratch| {
70        scratch.fill(3.5);
71        assert!(scratch.iter().all(|v| *v == 3.5));
72    });
73
74    // Bounded provisioning retains the working set while making geometric
75    // growth headroom reclaimable at a consumer-selected quiescent point.
76    let bounded_pool: ScratchPool<u32> = ScratchPool::new();
77    bounded_pool.with_scratch_bounded(1024, |_| {});
78    bounded_pool.with_scratch_bounded(1025, |_| {});
79    assert_eq!(bounded_pool.capacity(), 2048);
80    let retained = bounded_pool.release();
81    println!(
82        "bounded release: capacity={} -> retained={}",
83        2048, retained[0]
84    );
85    assert_eq!(retained[0], 1025);
86    bounded_pool.reset();
87    assert_eq!(bounded_pool.release()[0], 0);
88
89    println!("MAX_POOL_SLOTS = {MAX_POOL_SLOTS} (max concurrent nested borrows)");
90    println!("all scratch-pool assertions passed");
91}
Source

pub fn with_scratch_bounded<const INDEX: usize, R>( &self, n: usize, f: impl FnOnce(&mut [T]) -> R, ) -> R

Like with_scratch, but records the request so a later release can reclaim above the working set. See ScratchPool::with_scratch_bounded for the full contract.

§Panics

Panics when INDEX >= N.

Source

pub fn release(&self) -> [[usize; 4]; N]

Reclaims every pool’s storage above its recorded provision. See ScratchPool::release for the contract and the quiescent-calling rhythm this is designed for.

Returns the per-pool, per-slot capacities after reclamation.

Source

pub fn reset(&self)

Clears every pool’s recorded provisions so a later release reclaims every slot entirely. See ScratchPool::reset.

Source

pub fn capacity<const INDEX: usize>(&self) -> usize

Returns the primary capacity for slot INDEX.

Forwards to ScratchPool::capacity and inherits its contract: the figure is readable at any time, including from inside a live Self::with_scratch borrow of the same slot.

§Panics

Panics when INDEX >= N.

Source

pub fn slot_capacity<const INDEX: usize>(&self, slot: usize) -> usize

Returns the capacity of slot slot in pool INDEX.

This is the bank-level equivalent of ScratchPool::slot_capacity. It keeps the per-slot mirror visible without requiring a live borrow to the slot itself, which makes hot-loop capacity accounting and warmup tuning easier to reason about while preserving the same zero-copy semantics.

§Panics

Panics when INDEX >= N or slot >= MAX_POOL_SLOTS.

Source

pub fn borrow_depth<const INDEX: usize>(&self) -> u8

Returns the current borrow depth for slot INDEX.

§Panics

Panics when INDEX >= N.

Source

pub unsafe fn with_scratch_uninit<const INDEX: usize, R>( &self, n: usize, f: impl FnOnce(*mut [T]) -> R, ) -> R

Provides uninitialized scratch from slot INDEX to f.

Like with_scratch but skips the zero- initialization of newly grown capacity. The caller must write every element of the returned slice before reading it.

§Safety

Every element of the returned *mut [T] slice must be initialized before any safe read through as_slice, Deref, or similar.

§Panics

Panics when INDEX >= N.

Source

pub fn prewarm<const INDEX: usize>(&self, min_capacity: usize)

Prewarms slot INDEX to at least min_capacity elements.

Forwards to ScratchPool::prewarm. No-op when borrowed.

§Panics

Panics when INDEX >= N.

Source

pub fn preload(&self, sizes: &[usize])

Batch-prewarms every pool in the bank using the per-pool slot layout.

sizes[i] is interpreted as the minimum capacity for the ith slot of every pool inside this bank. The method mirrors ScratchPool::preload: out-of-range entries are ignored and zero-sized entries are skipped. This lets a caller warm the entire bank in one call while keeping the same single-threaded, zero-copy semantics.

Source

pub fn total_capacity_bytes<const INDEX: usize>(&self) -> usize

Returns the total backing bytes for slot INDEX across all of its slots.

§Panics

Panics when INDEX >= N.

Source

pub fn shrink_all_slots(&self)

Releases every slot in every pool without tearing down the bank itself.

This is the bank-level companion to ScratchPool::shrink_all_slots. It is useful for workload boundaries where the allocator should drop all warm scratch allocations before switching to a new transform pipeline.

Trait Implementations§

Source§

impl<T, const N: usize> Default for ScratchBank<T, N>
where T: ScratchElement,

Source§

fn default() -> ScratchBank<T, N>

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

impl<T, const N: usize> Send for ScratchBank<T, N>
where T: ScratchElement,

Auto Trait Implementations§

§

impl<T, const N: usize> !Freeze for ScratchBank<T, N>

§

impl<T, const N: usize> !RefUnwindSafe for ScratchBank<T, N>

§

impl<T, const N: usize> !Sync for ScratchBank<T, N>

§

impl<T, const N: usize> Unpin for ScratchBank<T, N>
where [ScratchPool<T>; N]: Unpin,

§

impl<T, const N: usize> UnsafeUnpin for ScratchBank<T, N>
where [ScratchPool<T>; N]: UnsafeUnpin,

§

impl<T, const N: usize> UnwindSafe for ScratchBank<T, N>
where [ScratchPool<T>; N]: UnwindSafe,

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where 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 T
where 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 T
where U: Into<T>,

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

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

Source§

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.