Please read the Implicit Freelist Allocator first.
Why Upgrade to Explicit?
The Implicit List Bottleneck: In the implicite freelist allocator, findfit loops through every single block in the heap (allocated or free) to find a free one. Time Complexity: _O(N) where N is the total number of heap blocks. So if we have 10GB of data allocated in tiny chunks, malloc will have to scan millions of headers just to find one free slot. This is unacceptably slow.
The Explicit List creates a Doubly Linked List containing only the free blocks.Time Complexity: O(F) where F is the number of free blocks. If memory is full, F is small, so malloc is instant.
Architecture
- Payload Overlay: Free blocks store
nextandprevpointers inside the empty payload area.[ Header | PREV_PTR | NEXT_PTR | ... | Footer ]
- List Policy: LIFO - Newly freed blocks are inserted at the root of the list.
- Search Algorithm: First Fit on the Free List. We only scan free blocks.
- realloc added: Added
my_reallocfunction which smartly extends or shrinks instead of doing malloc-copy-free - We need to store
nextandprevpointers for our linked list. But we don’t want to waste extra RAM for them. - A free block has no user data. The “Payload” area is empty. The Trick: We store the next and prev pointers inside the empty payload of the free blocks.
Free blocks form a distinct Doubly Linked List. Allocated blocks are ignored during the search. The pointers (next, prev) are stored inside the empty payload of the free blocks.
HEAP START ROOT (free_list_p) HEAP END
| | |
v v v
+------+ +----------------+ +-------------+ +----------------+ +------+
| PROL | | Allocated | | FREE | | Allocated | | EPIL |
+------+ | | | | | | +------+
+----------------+ +-------------+ +----------------+
| Header: 32/1 | | Header:48/0 | | Header: 16/1 |
| [ User Data ] | | Next Ptr -----------------------------> NULL
NULL <----------------------------- Prev Ptr | | [ User Data ] |
| | | ... | | |
| Footer: 32/1 | | Footer:48/0 | | Footer: 16/1 |
+----------------+ +-------------+ +----------------+
^ ^ ^
| | |
(IGNORED) (VISITED) (IGNORED)
Pros & Cons
Fast Allocation: O(F) where F is number of free blocks. Much faster than implicit.
Splitting: Implemented block splitting to reduce internal fragmentation.
Complexity: Managing list pointers during splitting and coalescing is error-prone.
Min Block Size: Blocks must be at least 32 bytes (16 overhead + 16 pointers) to be freeable.
Benchmarking & Performance
The project includes a performance benchmark benchmark.c designed to demonstrate the critical difference between the Implicit and Explicit allocator architectures.
The Workload
The benchmark simulates a high-load system (like a game engine or web server) by performing 50,000 random operations:
- Traffic Pattern: 60% Allocation / 40% Free (Simulates a heap that grows over time).
- Stress Test: Random block sizes (1 byte to 1024 bytes) to intentionally cause external fragmentation and “holes” in the heap.
- Metric: Measures Operations Per Second (OPS) and Total Execution Time.
Performance Characteristics
- Implicit List: Performance degrades quadratically (O(N)) as the heap fills. As the number of allocated blocks increases,
mallocmust scan past all of them to find free space. - Explicit List: Maintains high, constant throughput (O(Free)). Performance remains stable even as the heap grows to 50MB+, as
malloconly traverses the dedicated list of free blocks.
Next steps
The next obvious step would be a Segregated Freelist allocator, but it operates on the same principles. The core difference is to seperate the freelists into buckets based on size which would lead to a chained hashmap kind of structure. This is more of a debugging job, and I not much to learn in implementation as it is a minor change, still very important ofc.
I will proceed with the Buddy and Slab Allocators.