Skip to content

Conversation

@devreal
Copy link
Contributor

@devreal devreal commented Nov 25, 2024

Currently, zone_malloc iterates linearly over its blocks, leading to O(N) complexity and significant overhead if all blocks are taken. We now use a red-black-tree to order lists of free blocks in a binary search tree. Lookup, insert, and delete are log(N). If all blocks are taken lookup is constant.

When allocating memory, we look for blocks or the requested size or the next larger size. In the latter case, the block is split and put back into the tree, potentially allocating a new node.
When releasing a block, it is either inserted directly or merged with its predecessor/successor blocks, which are then removed and reinserted as a larger block.

In the previous scheme, freeing was of O(1) complexity, which is now O(log(N)). However, the better worst-case complexity will amortize this. Plus, using a binary search tree will help reuse of same-size fragments and avoids splitting larger segements when possible.

RB trees have lower average complexity than AVL trees and are widely used (C++ std::map, Linux kernel allocator).

@devreal devreal requested a review from a team as a code owner November 25, 2024 20:18
parsec_rbtree_node_construct, NULL);

void parsec_rbtree_init(parsec_rbtree_t* tree, size_t offset) {
tree->nil = PARSEC_OBJ_NEW(parsec_rbtree_node_t);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of allocating the nil you can make it point to the tree itself. The code only uses it to compare for end, of branches, so it should work just fine.

@bosilca
Copy link
Contributor

bosilca commented Nov 25, 2024

It would be interesting to see a comparison with the current allocator.

Currently, zone_malloc iterates linearly over its blocks, leading to
O(N) complexity and significant overhead if all blocks are taken.
We now use a red-black-tree to order lists of free blocks in a
binary search tree. Lookup, insert, and delete are log(N). If all
blocks are taken lookup is constant.

When allocating memory, we look for blocks or the requested size or
the next larger size. In the latter case, the block is split and put back
into the tree, potentially allocating a new node.
When releasing a block, it is either inserted directly or merged with its
predecessor/successor blocks, which are then removed and reinserted as a larger
block.

In the previous scheme, freeing had O(1) complexity, which is now O(log(N)).
However, the vastly better worst-case complexity will amortize this.
Plus, using a binary search tree will help reuse of same-size fragments
and avoids splitting larger segements when possible.

RB trees have lower average complexity than AVL trees and are widely used
(C++ std::map, Linux kernel allocator).

Signed-off-by: Joseph Schuchart <joseph.schuchart@stonybrook.edu>
Signed-off-by: Joseph Schuchart <joseph.schuchart@stonybrook.edu>
Zone allocators are used exclusively single-threaded.

Signed-off-by: Joseph Schuchart <joseph.schuchart@stonybrook.edu>
@devreal devreal force-pushed the zone-malloc-rbtree branch 2 times, most recently from 8a69809 to 89f418a Compare January 31, 2025 01:10
Signed-off-by: Joseph Schuchart <joseph.schuchart@stonybrook.edu>
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.

2 participants