Skip to content

mem: heap allocator#8

Merged
tspader merged 1 commit into
mainfrom
mem/heap-allocator
Jun 10, 2026
Merged

mem: heap allocator#8
tspader merged 1 commit into
mainfrom
mem/heap-allocator

Conversation

@tspader

@tspader tspader commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Implements a reasonably simple heap allocator. Small (less than ~2KB) allocations are rounded up to a fixed size bucket. These allocations are satisfied from 4KB pages of memory which is chunked by this bucket size. These chunks are linked, using an intrusive pointer in the chunk itself, to form a free list.

In addition to the granular free list inside a span, the heap stores spans themselves in a free list. On initialization, it asks the OS for 64KB of memory, splits it into 4KB spans, and links them. When a bucket doesn't have any more memory to serve a request, it claims a span.

When a span becomes empty, it is returned to the free list. It is not eagerly returned to the OS; only when the heap itself is destroyed. This means that the footprint of the heap is equal to its high watermark.
In return for this tradeoff, the allocator's overhead is strictly a function of (a) this watermark at any given time and (b) the number of allocations and their sizes. Compare this to malloc(), in which the order of allocation also gives different fragmentation patterns.

I also added some small things:

  • I pulled in a small fork of ubench.h that I've been working on, which is not in a releasable state but is usable
  • I ported the existing sp_glob.h benchmark
  • I wrote some benchmarks for the heap allocator to ballpark it against glibc, but ended up not using the framework
  • I wrote a small table renderer for the benchmarks
  • WIP: I moved the existing hash table benchmark to WIP
  • Unrelated: removed SP_MEM_ARENA_BLOCK_SIZE
  • Unrelated: reordered the mem section of the header so that the docs for the heap allocator (and by extension other structs) could be colocated with its and only its header
  • Remove: sp_mem_os_alloc_zero(), vestigial

Implements a reasonably simple heap allocator. Small (less than ~2KB)
allocations are rounded up to a fixed size bucket. These allocations are
satisfied from 4KB pages of memory which is chunked by this bucket size.
These chunks are linked, using an intrusive pointer in the chunk itself,
to form a free list.

In addition to the granular free list inside a span, the heap stores
spans themselves in a free list. On initialization, it asks the OS
for 64KB of memory, splits it into 4KB spans, and links them. When a
bucket doesn't have any more memory to serve a request, it claims a
span.

When a span becomes empty, it is returned to the free list. It is not
eagerly returned to the OS; only when the heap itself is destroyed.
This means that the footprint of the heap is equal to its high
watermark.
In return for this tradeoff, the allocator's overhead is strictly a
function of (a) this watermark at any given time and (b) the number of
allocations and their sizes. Compare this to malloc(), in which the
*order* of allocation also gives different fragmentation patterns.

I also added some small things:
- I pulled in a small fork of ubench.h that I've been working on,
which is not in a releasable state but is usable
- I ported the existing sp_glob.h benchmark
- I wrote some benchmarks for the heap allocator to ballpark it
against glibc, but ended up not using the framework
- I wrote a small table renderer for the benchmarks
- WIP: I moved the existing hash table benchmark to WIP
- Unrelated: removed SP_MEM_ARENA_BLOCK_SIZE
- Unrelated: reordered the mem section of the header so that the
docs for the heap allocator (and by extension other structs) could
be colocated with its and only its header
- Remove: sp_mem_os_alloc_zero(), vestigial
@tspader tspader merged commit 828cc5a into main Jun 10, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant