From c0e17808d46be70b461c020c9c320349e2f99ad9 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 13:26:09 +1100 Subject: [PATCH] [mem]: Split alloc and alloc_non_zeroed for buddy allocator --- core/mem/allocators.odin | 59 ++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 1efc60033..45c80e678 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1133,7 +1133,6 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { // Keep looping until there are no more buddies to coalesce block := head buddy := buddy_block_next(block) - no_coalescence := true for block < tail && buddy < tail { // make sure the buddies are within the range if block.is_free && buddy.is_free && block.size == buddy.size { @@ -1156,7 +1155,6 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { } } } - if no_coalescence { return } @@ -1166,17 +1164,14 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { @(require_results) buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Block { assert(size != 0) - best_block: ^Buddy_Block block := head // left buddy := buddy_block_next(block) // right - // The entire memory section between head and tail is free, // just call 'buddy_block_split' to get the allocation if buddy == tail && block.is_free { return buddy_block_split(block, size) } - // Find the block which is the 'best_block' to requested allocation sized for block < tail && buddy < tail { // make sure the buddies are within the range // If both buddies are free, coalesce them together @@ -1187,7 +1182,6 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl if size <= block.size && (best_block == nil || block.size <= best_block.size) { best_block = block } - block = buddy_block_next(buddy) if block < tail { // Delay the buddy block for the next iteration @@ -1195,20 +1189,16 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl } continue } - - if block.is_free && size <= block.size && (best_block == nil || block.size <= best_block.size) { best_block = block } - if buddy.is_free && size <= buddy.size && (best_block == nil || buddy.size < best_block.size) { // If each buddy are the same size, then it makes more sense // to pick the buddy as it "bounces around" less best_block = buddy } - if (block.size <= buddy.size) { block = buddy_block_next(buddy) if (block < tail) { @@ -1221,12 +1211,10 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl buddy = buddy_block_next(buddy) } } - if best_block != nil { // This will handle the case if the 'best_block' is also the perfect fit return buddy_block_split(best_block, size) } - // Maybe out of memory return nil } @@ -1245,26 +1233,20 @@ buddy_allocator :: proc(b: ^Buddy_Allocator) -> Allocator { } } -buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint) { +buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint, loc := #caller_location) { assert(data != nil) - assert(is_power_of_two(uintptr(len(data)))) - assert(is_power_of_two(uintptr(alignment))) - + assert(is_power_of_two(uintptr(len(data))), "Size of the backing buffer must be power of two", loc) + assert(is_power_of_two(uintptr(alignment)), "Alignment must be a power of two", loc) alignment := alignment if alignment < size_of(Buddy_Block) { alignment = size_of(Buddy_Block) } - ptr := raw_data(data) - assert(uintptr(ptr) % uintptr(alignment) == 0, "data is not aligned to minimum alignment") - + assert(uintptr(ptr) % uintptr(alignment) == 0, "data is not aligned to minimum alignment", loc) b.head = (^Buddy_Block)(ptr) - b.head.size = len(data) b.head.is_free = true - b.tail = buddy_block_next(b.head) - b.alignment = alignment } @@ -1274,19 +1256,25 @@ buddy_block_size_required :: proc(b: ^Buddy_Allocator, size: uint) -> uint { actual_size := b.alignment size += size_of(Buddy_Block) size = align_forward_uint(size, b.alignment) - for size > actual_size { actual_size <<= 1 } - return actual_size } @(require_results) -buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint, zeroed: bool) -> ([]byte, Allocator_Error) { +buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { + bytes, err := buddy_allocator_alloc_non_zeroed(b, size) + if bytes != nil { + zero_slice(bytes) + } + return bytes, err +} + +@(require_results) +buddy_allocator_alloc_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { if size != 0 { actual_size := buddy_block_size_required(b, size) - found := buddy_block_find_best(b.head, b.tail, actual_size) if found != nil { // Try to coalesce all the free buddy blocks and then search again @@ -1297,32 +1285,28 @@ buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint, zeroed: bool) -> return nil, .Out_Of_Memory } found.is_free = false - data := ([^]byte)(found)[b.alignment:][:size] - if zeroed { - zero_slice(data) - } return data, nil } return nil, nil } +@(require_results) buddy_allocator_free :: proc(b: ^Buddy_Allocator, ptr: rawptr) -> Allocator_Error { if ptr != nil { if !(b.head <= ptr && ptr <= b.tail) { return .Invalid_Pointer } - block := (^Buddy_Block)(([^]byte)(ptr)[-b.alignment:]) block.is_free = true - buddy_block_coalescence(b.head, b.tail) } return nil } buddy_allocator_proc :: proc( - allocator_data: rawptr, mode: Allocator_Mode, + allocator_data: rawptr, + mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, @@ -1330,10 +1314,11 @@ buddy_allocator_proc :: proc( ) -> ([]byte, Allocator_Error) { b := (^Buddy_Allocator)(allocator_data) - switch mode { - case .Alloc, .Alloc_Non_Zeroed: - return buddy_allocator_alloc(b, uint(size), mode == .Alloc) + case .Alloc: + return buddy_allocator_alloc(b, uint(size)) + case .Alloc_Non_Zeroed: + return buddy_allocator_alloc_non_zeroed(b, uint(size)) case .Resize: return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, buddy_allocator(b)) case .Resize_Non_Zeroed: @@ -1341,13 +1326,11 @@ buddy_allocator_proc :: proc( case .Free: return nil, buddy_allocator_free(b, old_memory) case .Free_All: - alignment := b.alignment head := ([^]byte)(b.head) tail := ([^]byte)(b.tail) data := head[:ptr_sub(tail, head)] buddy_allocator_init(b, data, alignment) - case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil {