Remove linear search for blocks when allocating

This commit is contained in:
2024-05-08 20:25:37 +02:00
parent b552dd4d11
commit 237a2afd91
2 changed files with 53 additions and 36 deletions
+41 -29
View File
@@ -166,16 +166,14 @@
#define MIN_BLOCK_SIZE BUDDY_ALLOC_MIN_BLOCK_SIZE #define MIN_BLOCK_SIZE BUDDY_ALLOC_MIN_BLOCK_SIZE
#define MAX_BLOCK_ALIGN_MASK (MAX_BLOCK_SIZE - 1) #define MAX_BLOCK_ALIGN_MASK (MAX_BLOCK_SIZE - 1)
struct page { struct page *next; };
/* /*
* Gets the address of the i-th page of the memory pool. * Gets the address of the i-th page of the memory pool.
* In this context, a page is a block of size MAX_BLOCK_SIZE. * In this context, a page is a block of size MAX_BLOCK_SIZE.
*/ */
static struct page* static struct buddy_page*
page_index_to_ptr(char *base, int i) page_index_to_ptr(char *base, int i)
{ {
return (struct page*) (base + (i << MAX_BLOCK_LOG2)); return (struct buddy_page*) (base + (i << MAX_BLOCK_LOG2));
} }
static struct buddy_alloc startup_empty() static struct buddy_alloc startup_empty()
@@ -231,15 +229,23 @@ struct buddy_alloc buddy_startup(char *base, size_t size,
/* /*
* Make the linked list of pages * Make the linked list of pages
*/ */
struct page *head = NULL; struct buddy_page *head = NULL;
struct page **tail = &head; struct buddy_page *tail = NULL;
int num_pages = size >> MAX_BLOCK_LOG2; int num_pages = size >> MAX_BLOCK_LOG2;
for (int i = 0; i < num_pages; i++) { for (int i = 0; i < num_pages; i++) {
struct page *p = page_index_to_ptr(base, i);
*tail = p; struct buddy_page *p = page_index_to_ptr(base, i);
tail = &p->next;
if (head) {
tail->next = p;
p->prev = tail;
} else {
head = p;
p->prev = NULL;
}
tail = p;
p->next = NULL;
} }
*tail = NULL;
/* /*
* Initialize the page info. The page_info bits are 0 when * Initialize the page info. The page_info bits are 0 when
@@ -512,17 +518,15 @@ remove_sibling_from_list(struct buddy_alloc *alloc,
int i, void *ptr) int i, void *ptr)
{ {
size_t len = 1U << (i + MIN_BLOCK_LOG2); size_t len = 1U << (i + MIN_BLOCK_LOG2);
struct page *sibling = (struct page*) sibling_of(ptr, len); struct buddy_page *sibling = (struct buddy_page*) sibling_of(ptr, len);
struct page *curs = (struct page*) alloc->lists[i];
struct page **prev = (struct page**) &alloc->lists[i]; if (sibling->prev)
while (curs != (struct page*) sibling) { sibling->prev->next = sibling->next;
assert(curs); else
prev = &curs->next; alloc->lists[i] = sibling->next;
curs = curs->next;
assert(curs); if (sibling->next)
} sibling->next->prev = sibling->prev;
assert(sibling == curs);
*prev = sibling->next;
} }
/* /*
@@ -537,21 +541,29 @@ static void append(struct buddy_alloc *alloc,
{ {
assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS); assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS);
struct page *pag = ptr; struct buddy_page *page = ptr;
pag->next = alloc->lists[i]; if (alloc->lists[i])
alloc->lists[i] = pag; alloc->lists[i]->prev = page;
page->prev = NULL;
page->next = alloc->lists[i];
alloc->lists[i] = page;
} }
static char *pop(struct buddy_alloc *alloc, int i) static char *pop(struct buddy_alloc *alloc, int i)
{ {
assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS); assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS);
char *ptr = alloc->lists[i];
assert(ptr);
alloc->lists[i] = ((struct page*) ptr)->next; struct buddy_page *page = alloc->lists[i];
return ptr; assert(page);
alloc->lists[i] = page->next;
if (alloc->lists[i])
alloc->lists[i]->prev = NULL;
return (char*) page;
} }
void *buddy_malloc(struct buddy_alloc *alloc, size_t len) void *buddy_malloc(struct buddy_alloc *alloc, size_t len)
+12 -7
View File
@@ -29,20 +29,20 @@
/* /*
* This is the minimum and maximum block size. The allocator uses free * This is the minimum and maximum block size. The allocator uses doubly
* lists to keep track of unused blocks, so a block must be at least * linked free lists to keep track of unused blocks, so a block must be
* the size of a pointer. We assume a pointer is 8 bytes long, so the * at least the size of two pointers. We assume a pointer is 8 bytes long,
* minimum value must be greater or equal to 3 (log2(8) = 3). * so the minimum value must be greater or equal to 4 (log2(2*8) = 4).
* *
* For the maximum value there is really no downside in making it big, * For the maximum value there is really no downside in making it big,
* except for the fact that the pool provided by the user should at * except for the fact that the pool provided by the user should at
* least be that big. * least be that big.
*/ */
#define BUDDY_ALLOC_MAX_BLOCK_LOG2 13 #define BUDDY_ALLOC_MAX_BLOCK_LOG2 13
#define BUDDY_ALLOC_MIN_BLOCK_LOG2 3 #define BUDDY_ALLOC_MIN_BLOCK_LOG2 4
_Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 <= BUDDY_ALLOC_MAX_BLOCK_LOG2); _Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 <= BUDDY_ALLOC_MAX_BLOCK_LOG2);
_Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 > 2); _Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 > 3);
#define BUDDY_ALLOC_NUM_LISTS (BUDDY_ALLOC_MAX_BLOCK_LOG2 - BUDDY_ALLOC_MIN_BLOCK_LOG2 + 1) #define BUDDY_ALLOC_NUM_LISTS (BUDDY_ALLOC_MAX_BLOCK_LOG2 - BUDDY_ALLOC_MIN_BLOCK_LOG2 + 1)
#define BUDDY_ALLOC_MAX_BLOCK_SIZE (1U << BUDDY_ALLOC_MAX_BLOCK_LOG2) #define BUDDY_ALLOC_MAX_BLOCK_SIZE (1U << BUDDY_ALLOC_MAX_BLOCK_LOG2)
@@ -67,10 +67,15 @@ struct page_info {
uint32_t bits[BUDDY_ALLOC_WORDS_PER_PAGE]; uint32_t bits[BUDDY_ALLOC_WORDS_PER_PAGE];
}; };
struct buddy_page {
struct buddy_page *prev;
struct buddy_page *next;
};
struct buddy_alloc { struct buddy_alloc {
void *base; void *base;
size_t size; size_t size;
void *lists[BUDDY_ALLOC_NUM_LISTS]; struct buddy_page *lists[BUDDY_ALLOC_NUM_LISTS];
struct page_info *info; struct page_info *info;
int num_info; int num_info;
}; };