From b4c6fd1866e94016c6d91dc572720d165919fde4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 12 Mar 2024 02:32:16 -0400 Subject: [PATCH] More fixes & performance improvements Still havent figured out the realloc issue with virtual arenas... But the fixes the slab perforamnce mitigate it at least. --- code/api.odin | 4 +-- code/grime.odin | 2 ++ code/grime_array.odin | 20 +++++++++------ code/grime_hashmap_zpl.odin | 2 +- code/grime_pool_allocator.odin | 42 +++----------------------------- code/grime_slab_allocator.odin | 1 + code/grime_string_interning.odin | 2 +- code/grime_virtual_arena.odin | 27 ++++++++++++++++---- code/parser_whitespace.odin | 9 +++---- code/tick_update.odin | 9 ++++--- code/ui.odin | 4 +-- 11 files changed, 58 insertions(+), 64 deletions(-) diff --git a/code/api.odin b/code/api.odin index 5f34bf7..1623e28 100644 --- a/code/api.odin +++ b/code/api.odin @@ -249,8 +249,8 @@ reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, // Thankfully persistent dynamic allocations are rare, and thus we know exactly which ones they are. font_provider_data := & state.font_provider_data - font_provider_data.font_cache.hashes.allocator = persistent_slab_allocator() - font_provider_data.font_cache.entries.allocator = persistent_slab_allocator() + font_provider_data.font_cache.hashes.backing = persistent_slab_allocator() + font_provider_data.font_cache.entries.backing = persistent_slab_allocator() ui_reload( & get_state().project.workspace.ui, cache_allocator = persistent_slab_allocator() ) diff --git a/code/grime.odin b/code/grime.odin index 4e07225..f1d2a0a 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -37,6 +37,7 @@ import "core:mem" AllocatorModeSet :: mem.Allocator_Mode_Set alloc :: mem.alloc alloc_bytes :: mem.alloc_bytes + alloc_bytes_non_zeroed :: mem.alloc_bytes_non_zeroed Arena :: mem.Arena arena_allocator :: mem.arena_allocator arena_init :: mem.arena_init @@ -46,6 +47,7 @@ import "core:mem" is_power_of_two_uintptr :: mem.is_power_of_two ptr_offset :: mem.ptr_offset resize :: mem.resize + reisze_non_zeroed :: mem.default_resize_bytes_align_non_zeroed slice_ptr :: mem.slice_ptr TrackingAllocator :: mem.Tracking_Allocator tracking_allocator :: mem.tracking_allocator diff --git a/code/grime_array.odin b/code/grime_array.odin index 0646990..dfc6fc7 100644 --- a/code/grime_array.odin +++ b/code/grime_array.odin @@ -8,14 +8,14 @@ import "core:mem" import "core:slice" // Array :: struct ( $ Type : typeid ) { -// allocator : Allocator, +// bakcing : Allocator, // capacity : u64, // num : u64, // data : [^]Type, // } ArrayHeader :: struct ( $ Type : typeid ) { - allocator : Allocator, + backing : Allocator, capacity : u64, num : u64, data : [^]Type, @@ -62,7 +62,7 @@ array_init_reserve :: proc if alloc_error != AllocatorError.None do return result.header = cast( ^ArrayHeader(Type)) raw_mem; - result.allocator = allocator + result.backing = allocator result.capacity = capacity result.data = cast( [^]Type ) (cast( [^]ArrayHeader(Type)) result.header)[ 1:] return @@ -201,13 +201,13 @@ array_fill :: proc( using self : Array( $ Type ), begin, end : u64, value : Type } array_free :: proc( using self : Array( $ Type ) ) { - free( data, allocator ) + free( data, backing ) self.data = nil } array_grow :: proc( using self : ^Array( $ Type ), min_capacity : u64 ) -> AllocatorError { - profile(#procedure) + // profile(#procedure) new_capacity := array_grow_formula( capacity ) if new_capacity < min_capacity { @@ -269,13 +269,19 @@ array_set_capacity :: proc( self : ^Array( $ Type ), new_capacity : u64 ) -> All new_size := header_size + cast(int) new_capacity * size_of(Type) old_size := header_size + cast(int) self.capacity * size_of(Type) - new_mem, result_code := resize( self.header, old_size, new_size, allocator = self.allocator ) + // new_mem, result_code := resize( self.header, old_size, new_size, allocator = self.backing ) + new_mem, result_code := reisze_non_zeroed( byte_slice( self.header, old_size), new_size, mem.DEFAULT_ALIGNMENT, allocator = self.backing ) + if result_code != AllocatorError.None { ensure( false, "Failed to allocate for new array capacity" ) return result_code } + if new_mem == nil { + ensure(false, "new_mem is nil but no allocation error") + return result_code + } - self.header = cast( ^ArrayHeader(Type)) new_mem; + self.header = cast( ^ArrayHeader(Type)) raw_data(new_mem); self.data = cast( [^]Type ) (cast( [^]ArrayHeader(Type)) self.header)[ 1:] self.capacity = new_capacity self.num = self.num diff --git a/code/grime_hashmap_zpl.odin b/code/grime_hashmap_zpl.odin index ca46485..d8f860c 100644 --- a/code/grime_hashmap_zpl.odin +++ b/code/grime_hashmap_zpl.odin @@ -119,7 +119,7 @@ zpl_hmap_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorE ensure( false, "ZPL HMAP IS REHASHING" ) last_added_index : i64 - new_ht, init_result := zpl_hmap_init_reserve( Type, ht.hashes.allocator, new_num ) + new_ht, init_result := zpl_hmap_init_reserve( Type, ht.hashes.backing, new_num ) if init_result != AllocatorError.None { ensure( false, "New zpl_hmap failed to allocate" ) return init_result diff --git a/code/grime_pool_allocator.odin b/code/grime_pool_allocator.odin index 26ac9c2..187a86f 100644 --- a/code/grime_pool_allocator.odin +++ b/code/grime_pool_allocator.odin @@ -43,12 +43,6 @@ Pool_FreeBlock :: struct { Pool_Check_Release_Object_Validity :: true -pool_allocator :: proc ( using self : Pool ) -> (allocator : Allocator) { - allocator.procedure = pool_allocator_proc - allocator.data = self.header - return -} - pool_init :: proc ( block_size : uint, bucket_capacity : uint, @@ -98,12 +92,12 @@ pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> Alloca header_size := cast(uint) align_forward_int( size_of(PoolBucket), int(alignment)) to_allocate := cast(int) (header_size + bucket_capacity * num_buckets) - bucket_memory, alloc_error := alloc( to_allocate, int(alignment), backing ) + bucket_memory, alloc_error := alloc_bytes_non_zeroed( to_allocate, int(alignment), backing ) if alloc_error != .None { return alloc_error } - next_bucket_ptr := cast( [^]byte) bucket_memory + next_bucket_ptr := cast( [^]byte) raw_data(bucket_memory) for index in 0 ..< num_buckets { bucket := cast( ^PoolBucket) next_bucket_ptr @@ -151,6 +145,7 @@ pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : Alloca { alloc_error = pool_allocate_buckets( pool, 1 ) if alloc_error != .None { + ensure(false, "Failed to allocate bucket") return } pool.current_bucket = bucket_list.first @@ -175,6 +170,7 @@ pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : Alloca { alloc_error := pool_allocate_buckets( pool, 1 ) if alloc_error != .None { + ensure(false, "Failed to allocate bucket") return } pool.current_bucket = pool.current_bucket.next @@ -243,33 +239,3 @@ pool_validate_ownership :: proc( using self : Pool, block : [] byte ) -> b32 return within_bucket } - -// This interface should really not be used for a pool allocator... But fk it its here. -// TODO(Ed): Implement this eventaully.. -pool_allocator_proc :: proc( - allocator_data : rawptr, - mode : AllocatorMode, - size : int, - alignment : int, - old_memory : rawptr, - old_size : int, - loc := #caller_location -) -> ([]byte, AllocatorError) -{ - switch mode - { - case .Alloc, .Alloc_Non_Zeroed: - fallthrough - case .Free: - fallthrough - case .Free_All: - fallthrough - case .Resize, .Resize_Non_Zeroed: - fallthrough - case .Query_Features: - fallthrough - case .Query_Info: - fallthrough - } - return nil, AllocatorError.Mode_Not_Implemented -} diff --git a/code/grime_slab_allocator.odin b/code/grime_slab_allocator.odin index 43302ee..bbe55a3 100644 --- a/code/grime_slab_allocator.odin +++ b/code/grime_slab_allocator.odin @@ -75,6 +75,7 @@ slab_init :: proc( policy : ^SlabPolicy, bucket_reserve_num : uint = 0, allocato slab_init_pools :: proc ( using self : Slab, bucket_reserve_num : uint = 0 ) -> AllocatorError { + profile(#procedure) for id in 0 ..< policy.idx { using size_class := policy.items[id] diff --git a/code/grime_string_interning.odin b/code/grime_string_interning.odin index 0d112ac..73848dd 100644 --- a/code/grime_string_interning.odin +++ b/code/grime_string_interning.odin @@ -75,7 +75,7 @@ str_intern :: proc( { length := len(content) // str_mem, alloc_error := alloc( length, mem.DEFAULT_ALIGNMENT ) - str_mem, alloc_error := slab_alloc( cache.slab, uint(length), uint(mem.DEFAULT_ALIGNMENT) ) + str_mem, alloc_error := slab_alloc( cache.slab, uint(length), uint(mem.DEFAULT_ALIGNMENT), zero_memory = false ) verify( alloc_error == .None, "String cache had a backing allocator error" ) // copy_non_overlapping( str_mem, raw_data(content), length ) diff --git a/code/grime_virtual_arena.odin b/code/grime_virtual_arena.odin index 32663d9..37496f6 100644 --- a/code/grime_virtual_arena.odin +++ b/code/grime_virtual_arena.odin @@ -100,13 +100,16 @@ varena_alloc :: proc( using self : ^VArena, requested_size := size if requested_size == 0 { + ensure(false, "Requested 0 size") return nil, .Invalid_Argument } + // ensure( requested_size > page_size, "Requested less than a page size, going to allocate a page size") + // requested_size = max(requested_size, page_size) sync.mutex_guard( & mutex ) alignment_offset := uint(0) - current_offset := cast(uintptr) reserve_start[ commit_used:] + current_offset := uintptr(self.reserve_start) + uintptr(commit_used) mask := uintptr(alignment - 1) if current_offset & mask != 0 { @@ -140,10 +143,9 @@ varena_alloc :: proc( using self : ^VArena, } } - aligned_start := uintptr(self.commit_used + alignment_offset) - data_ptr := rawptr(uintptr( reserve_start ) + aligned_start) + data_ptr := rawptr(current_offset + uintptr(alignment_offset)) data = byte_slice( data_ptr, int(requested_size) ) - commit_used += size_to_allocate + self.commit_used += size_to_allocate alloc_error = .None if zero_memory { @@ -182,6 +184,8 @@ varena_allocator_proc :: proc( alignment := uint(alignment) old_size := uint(old_size) + page_size := uint(virtual_get_page_size()) + switch mode { case .Alloc, .Alloc_Non_Zeroed: @@ -195,31 +199,43 @@ varena_allocator_proc :: proc( case .Resize, .Resize_Non_Zeroed: if old_memory == nil { + ensure(false, "Resizing without old_memory?") return varena_alloc( arena, size, alignment, (mode != .Resize_Non_Zeroed), location ) } if size == old_size { + ensure(false, "Requested resize when none needed") data = byte_slice( old_memory, old_size ) return } alignment_offset := uintptr(old_memory) & uintptr(alignment - 1) if alignment_offset == 0 && size < old_size { + ensure(false, "Requested a shrink from a virtual arena") data = byte_slice( old_memory, size ) return } old_memory_offset := uintptr(old_memory) + uintptr(old_size) current_offset := uintptr(arena.reserve_start) + uintptr(arena.commit_used) + + // if old_size < page_size { + // // We're dealing with an allocation that requested less than the minimum allocated on vmem. + // // Provide them more of their actual memory + // data = byte_slice( old_memory, size ) + // return + // } + verify( old_memory_offset == current_offset || arena.allow_any_reize, "Cannot resize existing allocation in vitual arena to a larger size unless it was the last allocated" ) - if old_memory_offset == current_offset && arena.allow_any_reize + if old_memory_offset != current_offset && arena.allow_any_reize { // Give it new memory and copy the old over. Old memory is unrecoverable until clear. new_region : []byte new_region, alloc_error = varena_alloc( arena, size, alignment, (mode != .Resize_Non_Zeroed), location ) if new_region == nil || alloc_error != .None { + ensure(false, "Failed to grab new region") data = byte_slice( old_memory, old_size ) return } @@ -232,6 +248,7 @@ varena_allocator_proc :: proc( new_region : []byte new_region, alloc_error = varena_alloc( arena, size - old_size, alignment, (mode != .Resize_Non_Zeroed), location ) if new_region == nil || alloc_error != .None { + ensure(false, "Failed to grab new region") data = byte_slice( old_memory, old_size ) return } diff --git a/code/parser_whitespace.odin b/code/parser_whitespace.odin index 0ab9ee4..89a3dd7 100644 --- a/code/parser_whitespace.odin +++ b/code/parser_whitespace.odin @@ -252,7 +252,7 @@ pws_parser_parse :: proc( text : string, allocator : Allocator ) -> ( PWS_ParseR nodes, alloc_error = array_init_reserve( PWS_AST, allocator, 8 ) verify( alloc_error == nil, "Allocation failure creating nodes array") - lines, alloc_error = array_init_reserve( ^PWS_AST, allocator, 8 ) + parser.lines, alloc_error = array_init_reserve( ^PWS_AST, allocator, 8 ) verify( alloc_error == nil, "Allocation failure creating line array") //region Helper procs @@ -301,12 +301,11 @@ pws_parser_parse :: proc( text : string, allocator : Allocator ) -> ( PWS_ParseR type = .Visible case .New_Line: - { eat_line() - alloc_error = array_append( & lines, prev_line ) + alloc_error = array_append( & parser.lines, prev_line ) verify( alloc_error == nil, "Allocation failure appending node") - } + case PWS_TokenType.End_Of_File: } @@ -357,7 +356,7 @@ pws_parser_parse :: proc( text : string, allocator : Allocator ) -> ( PWS_ParseR if line.first != nil { eat_line() - alloc_error = array_append( & lines, prev_line ) + alloc_error = array_append( & parser.lines, prev_line ) verify( alloc_error == nil, "Allocation failure appending node") } diff --git a/code/tick_update.odin b/code/tick_update.odin index c631d36..1f3eb10 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -238,8 +238,11 @@ update :: proc( delta_time : f64 ) -> b32 test_draggable() // test_text_box() + // Test Parenting + + // Whitespace AST test - when true + when false { profile("Whitespace AST test") @@ -267,7 +270,7 @@ update :: proc( delta_time : f64 ) -> b32 alloc_error : AllocatorError; success : bool // debug.lorem_content, success = os.read_entire_file( debug.path_lorem, frame_allocator() ) - // debug.lorem_parse, alloc_error = pws_parser_parse( transmute(string) debug.lorem_content, frame_allocator() ) + // debug.lorem_parse, alloc_error = pws_parser_parse( transmute(string) debug.lorem_content, frame_slab_allocator() ) // verify( alloc_error == .None, "Faield to parse due to allocation failure" ) text_space := str_intern( " " ) @@ -275,7 +278,7 @@ update :: proc( delta_time : f64 ) -> b32 // index := 0 widgets : Array(UI_Widget) - widgets, alloc_error = array_init( UI_Widget, frame_allocator() ) + widgets, alloc_error = array_init( UI_Widget, frame_slab_allocator() ) widgets_ptr := & widgets label_id := 0 diff --git a/code/ui.odin b/code/ui.odin index 54c32ed..69c8788 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -319,8 +319,8 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator ) { // We need to repopulate Allocator references for cache in & ui.caches { - cache.entries.allocator = cache_allocator - cache.hashes.allocator = cache_allocator + cache.entries.backing = cache_allocator + cache.hashes.backing = cache_allocator } }