Expand description
A size-classed free list over the system allocator.
Invariant: it recycles rather than accumulating. A bump arena that never frees is the shortest thing to write and the wrong thing to ship: thirty rounds of the gate’s plan would grow it without bound, and what it measured would be page faults rather than allocation. This keeps one intrusive list per size class, capped, and hands a block back to the system allocator when the class is full.
§Why the engine has one at all
Because allocation is where a compile goes. A trivial compile was measured
at 25 heap allocations, with the Windows C runtime heap at 59% of the
time, and a size-classed free list at 17% overall on the same
plan - which is why Phase 3’s Part E names it the cheapest first move rather
than one of the several structural changes beside it. The same shape is
visible outside compilation: CREATE INDEX over a hundred thousand rows
builds two allocations per row just to hold the key and the rowid, and the
gate’s schema.index spends more time in its scan than SQLite spends on the
whole statement.
What it removes is exactly what was in question: the size lookup, the
locking and the per-call bookkeeping the system allocator does. It does not
try to be a better allocator in general - above LARGEST and for any
alignment the system’s own guarantee does not cover, the request is
forwarded unchanged.
§Why it never allocates
An allocator that allocates re-enters itself, and a re-entrant allocator is
a deadlock or a stack overflow waiting for the right allocation pattern. So
the free lists are intrusive: a freed block holds the pointer to the
next free block of its class in its own first eight bytes, and the heads
live in a fixed-size array of Cells in thread-local storage. Nothing here
calls Vec, Box, or anything that could.
§Why it is a crate of its own
Because every other production crate in this workspace carries
#![forbid(unsafe_code)], and an allocator cannot. Putting it here keeps
that true everywhere it is true today and confines the unsafe to one file
that has nothing else in it - no dependencies, first-party or otherwise, so
there is nothing it could re-enter itself through.
§Why it is per thread
Because a shared list needs a lock, and the lock is most of what this exists to remove. A block allocated on one thread and freed on another goes onto the freeing thread’s list, which is safe - the block is memory of a known class, and the class is derived from the layout the caller hands back - and at worst moves a block between threads. The per-class cap bounds what that can cost.
Structs§
- Pooled
- A size-classed free list over the system allocator.
Constants§
- LARGEST
- The largest allocation the free list handles itself.