$npx -y skills add mohitmishra786/low-level-dev-skills --skill custom-allocatorsCustom allocator skill for memory allocation strategies. Use when implementing pool/slab/arena allocators, tuning jemalloc/mimalloc, writing Rust GlobalAlloc, or benchmarking allocator performance. Activates on queries about jemalloc, mimalloc, tcmalloc, arena allocator, GlobalAl
| 1 | # Custom Allocators |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | Guide agents through memory allocator design and tuning: pool/slab/arena/buddy taxonomy, jemalloc internals and `MALLOC_CONF`, mimalloc design, tcmalloc thread-caching, writing a simple pool allocator in C, Rust `GlobalAlloc` trait, fragmentation metrics, and benchmarking. |
| 6 | |
| 7 | ## When to Use |
| 8 | |
| 9 | - Replacing malloc for latency-sensitive or embedded workloads |
| 10 | - Tuning jemalloc/mimalloc for server heap behavior |
| 11 | - Implementing arena allocation for request-scoped or frame-based lifetimes |
| 12 | - Writing a Rust custom global allocator for `no_std` or performance |
| 13 | - Diagnosing heap fragmentation (internal vs external) |
| 14 | - Benchmarking allocator throughput and latency |
| 15 | |
| 16 | ## Workflow |
| 17 | |
| 18 | ### 1. Allocator taxonomy |
| 19 | |
| 20 | ``` |
| 21 | Allocator types |
| 22 | ├── Pool/fixed-size — O(1) alloc/free, fixed block sizes |
| 23 | ├── Slab — kernel-style, cache-friendly size classes |
| 24 | ├── Arena/bump — fast alloc, bulk free (reset arena) |
| 25 | ├── Buddy — power-of-two blocks, low fragmentation for large allocs |
| 26 | └── General (malloc) — jemalloc, mimalloc, tcmalloc |
| 27 | ``` |
| 28 | |
| 29 | ### 2. Simple pool allocator in C |
| 30 | |
| 31 | ```c |
| 32 | #include <stddef.h> |
| 33 | #include <stdint.h> |
| 34 | #include <stdlib.h> |
| 35 | |
| 36 | typedef struct pool_block { |
| 37 | struct pool_block *next; |
| 38 | } pool_block_t; |
| 39 | |
| 40 | typedef struct { |
| 41 | void *memory; |
| 42 | size_t block_size; |
| 43 | size_t num_blocks; |
| 44 | pool_block_t *free_list; |
| 45 | } pool_t; |
| 46 | |
| 47 | int pool_init(pool_t *p, size_t block_size, size_t num_blocks) { |
| 48 | p->block_size = block_size < sizeof(pool_block_t) ? |
| 49 | sizeof(pool_block_t) : block_size; |
| 50 | p->num_blocks = num_blocks; |
| 51 | p->memory = aligned_alloc(64, p->block_size * num_blocks); |
| 52 | if (!p->memory) return -1; |
| 53 | p->free_list = NULL; |
| 54 | for (size_t i = 0; i < num_blocks; i++) { |
| 55 | pool_block_t *blk = (pool_block_t *)((char *)p->memory + i * p->block_size); |
| 56 | blk->next = p->free_list; |
| 57 | p->free_list = blk; |
| 58 | } |
| 59 | return 0; |
| 60 | } |
| 61 | |
| 62 | void *pool_alloc(pool_t *p) { |
| 63 | if (!p->free_list) return NULL; |
| 64 | pool_block_t *blk = p->free_list; |
| 65 | p->free_list = blk->next; |
| 66 | return blk; |
| 67 | } |
| 68 | |
| 69 | void pool_free(pool_t *p, void *ptr) { |
| 70 | pool_block_t *blk = (pool_block_t *)ptr; |
| 71 | blk->next = p->free_list; |
| 72 | p->free_list = blk; |
| 73 | } |
| 74 | ``` |
| 75 | |
| 76 | ### 3. Arena allocator |
| 77 | |
| 78 | ```c |
| 79 | typedef struct { |
| 80 | char *base; |
| 81 | size_t capacity; |
| 82 | size_t offset; |
| 83 | } arena_t; |
| 84 | |
| 85 | void *arena_alloc(arena_t *a, size_t size, size_t align) { |
| 86 | uintptr_t cur = (uintptr_t)(a->base + a->offset); |
| 87 | uintptr_t aligned = (cur + align - 1) & ~(align - 1); |
| 88 | size_t padding = aligned - cur; |
| 89 | if (a->offset + padding + size > a->capacity) |
| 90 | return NULL; |
| 91 | a->offset += padding + size; |
| 92 | return (void *)aligned; |
| 93 | } |
| 94 | |
| 95 | void arena_reset(arena_t *a) { a->offset = 0; } |
| 96 | ``` |
| 97 | |
| 98 | Use per-request arenas in servers — reset after response, no per-object free. |
| 99 | |
| 100 | ### 4. jemalloc tuning |
| 101 | |
| 102 | ```bash |
| 103 | # Use jemalloc as system allocator |
| 104 | LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 ./myapp |
| 105 | |
| 106 | # Runtime tuning |
| 107 | export MALLOC_CONF="background_thread:true,dirty_decay_ms:1000,muzzy_decay_ms:1000" |
| 108 | |
| 109 | # Profiling build |
| 110 | # ./configure --enable-prof |
| 111 | export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:19" |
| 112 | ``` |
| 113 | |
| 114 | jemalloc concepts: |
| 115 | - **Size classes** — rounded allocation sizes |
| 116 | - **tcache** — per-thread cache for hot sizes |
| 117 | - **arenas** — reduce contention across threads |
| 118 | |
| 119 | ```bash |
| 120 | # Statistics |
| 121 | export MALLOC_CONF="stats_print:true" |
| 122 | # prints on exit, or: |
| 123 | mallctl("epoch", ...); mallctl("stats.allocated", ...); |
| 124 | ``` |
| 125 | |
| 126 | ### 5. mimalloc |
| 127 | |
| 128 | ```bash |
| 129 | # Use mimalloc |
| 130 | LD_PRELOAD=/usr/lib/libmimalloc.so ./myapp |
| 131 | |
| 132 | # Environment options |
| 133 | export MIMALLOC_SHOW_STATS=1 |
| 134 | export MIMALLOC_PAGE_RESET=1 |
| 135 | ``` |
| 136 | |
| 137 | mimalloc hierarchy: segment → page → block. Generally lower metadata overhead than jemalloc. |
| 138 | |
| 139 | ```bash |
| 140 | # Statistics at exit |
| 141 | MIMALLOC_SHOW_STATS=1 ./myapp |
| 142 | ``` |
| 143 | |
| 144 | ### 6. tcmalloc thread-caching |
| 145 | |
| 146 | ```bash |
| 147 | LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc.so.4 ./myapp |
| 148 | |
| 149 | # Environment |
| 150 | export TCMALLOC_SAMPLE_PARAMETER=524288 # sampling profiler |
| 151 | ``` |
| 152 | |
| 153 | Per-thread caches for small objects; central heap for larger allocations. |
| 154 | |
| 155 | ### 7. Rust GlobalAlloc |
| 156 | |
| 157 | ```rust |
| 158 | use std::alloc::{GlobalAlloc, Layout, System}; |
| 159 | use std::sync::atomic::{AtomicUsize, Ordering}; |
| 160 | |
| 161 | struct TrackingAllocator; |
| 162 | |
| 163 | static ALLOCATED: AtomicUsize = AtomicUsize::new(0); |
| 164 | |
| 165 | unsafe impl GlobalAlloc for TrackingAllocator { |
| 166 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { |
| 167 | let ptr = System.alloc(layout); |
| 168 | if !ptr.is_null() { |
| 169 | ALLOCATED.fetch_add(layout.size(), Ordering::Relaxed); |
| 170 | } |
| 171 | ptr |
| 172 | } |
| 173 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { |
| 174 | System.dealloc(ptr, layout); |
| 175 | ALLOCATED.fetch_sub(layout.size(), Ordering::Relaxed); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | #[global_alloc |