Fixed a long-standing issue with the chained hashmap (finally)

This commit is contained in:
2024-06-21 00:26:29 -04:00
parent c405c47e6c
commit a560222d5d
9 changed files with 146 additions and 92 deletions

View File

@ -4,6 +4,8 @@ package VEFontCache
The choice was made to keep the LRU cache implementation as close to the original as possible.
*/
import "base:runtime"
PoolListIter :: i32
PoolListValue :: u64
@ -20,9 +22,10 @@ PoolList :: struct {
back : PoolListIter,
size : u32,
capacity : u32,
dbg_name : string,
}
pool_list_init :: proc( pool : ^PoolList, capacity : u32 )
pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = "" )
{
error : AllocatorError
pool.items, error = make( Array( PoolListItem ), u64(capacity) )
@ -35,6 +38,7 @@ pool_list_init :: proc( pool : ^PoolList, capacity : u32 )
pool.capacity = capacity
pool.dbg_name = dbg_name
using pool
for id in 0 ..< capacity {
@ -72,6 +76,9 @@ pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
items.data[ id ].prev = -1
items.data[ id ].next = front
items.data[ id ].value = value
if pool.dbg_name != "" {
logf("pool_list: pushed %v into id %v", value, id)
}
if front != -1 do items.data[ front ].prev = id
if back == -1 do back = id
@ -98,6 +105,9 @@ pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter )
iter_node.prev = -1
iter_node.next = -1
// if pool.dbg_name != "" {
// logf("pool_list: erased %v, at id %v", iter_node.value, iter)
// }
iter_node.value = 0
append( & free_list, iter )
@ -115,34 +125,35 @@ pool_list_peek_back :: proc ( pool : ^PoolList ) -> PoolListValue {
}
pool_list_pop_back :: proc( pool : ^PoolList ) -> PoolListValue {
using pool
if size <= 0 do return 0
assert( back != -1 )
if pool.size <= 0 do return 0
assert( pool.back != -1 )
value := items.data[ back ].value
pool_list_erase( pool, back )
value := pool.items.data[ pool.back ].value
pool_list_erase( pool, pool.back )
return value
}
LRU_Link :: struct {
pad_top : u64,
value : i32,
ptr : PoolListIter,
pad_bottom : u64,
}
LRU_Cache :: struct {
capacity : u32,
num : u32,
table : HMapZPL(LRU_Link),
table : HMapChained(LRU_Link),
key_queue : PoolList,
}
LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 ) {
LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) {
error : AllocatorError
cache.capacity = capacity
cache.table, error = hmap_zpl_init( HMapZPL(LRU_Link), u64( hmap_closest_prime( uint(capacity))) )
cache.table, error = make( HMapChained(LRU_Link), hmap_closest_prime( uint(capacity)) )
assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table")
pool_list_init( & cache.key_queue, capacity )
pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name )
}
LRU_free :: proc( cache : ^LRU_Cache )
@ -152,7 +163,7 @@ LRU_free :: proc( cache : ^LRU_Cache )
LRU_reload :: proc( cache : ^LRU_Cache, allocator : Allocator )
{
hmap_zpl_reload( & cache.table, allocator )
hmap_chained_reload( cache.table, allocator )
pool_list_reload( & cache.key_queue, allocator )
}
@ -162,9 +173,13 @@ LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) {
return
}
LRU_find :: proc( cache : ^LRU_Cache, key : u64 ) -> ^LRU_Link {
LRU_find :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> ^LRU_Link {
hash := LRU_hash_key( key )
link := get( & cache.table, hash )
link := get( cache.table, hash )
// if link == nil && must_find {
// runtime.debug_trap()
// link = get( cache.table, hash )
// }
return link
}
@ -186,8 +201,8 @@ LRU_get_next_evicted :: proc( cache : ^LRU_Cache ) -> u64
return 0xFFFFFFFFFFFFFFFF
}
LRU_peek :: proc( cache : ^LRU_Cache, key : u64 ) -> i32 {
iter := LRU_find( cache, key )
LRU_peek :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 {
iter := LRU_find( cache, key, must_find )
if iter == nil {
return -1
}
@ -197,7 +212,7 @@ LRU_peek :: proc( cache : ^LRU_Cache, key : u64 ) -> i32 {
LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
{
hash_key := LRU_hash_key( key )
iter := get( & cache.table, hash_key )
iter := get( cache.table, hash_key )
if iter != nil {
LRU_refresh( cache, key )
iter.value = value
@ -209,14 +224,19 @@ LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
evict = pool_list_pop_back( & cache.key_queue )
evict_hash := LRU_hash_key( evict )
hmap_zpl_remove( & cache.table, evict_hash )
// if cache.table.dbg_name != "" {
// logf("%v: Evicted %v with hash: %v", cache.table.dbg_name, evict, evict_hash)
// }
hmap_chained_remove( cache.table, evict_hash )
cache.num -= 1
}
pool_list_push_front( & cache.key_queue, key )
// if cache.table.dbg_name != "" {
// logf("%v: Pushed %v with hash: %v", cache.table.dbg_name, key, hash_key )
// }
// set( cache.table, hash_key, LRU_Link {
set( & cache.table, hash_key, LRU_Link {
set( cache.table, hash_key, LRU_Link {
value = value,
ptr = cache.key_queue.front
})
@ -227,6 +247,9 @@ LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) {
link := LRU_find( cache, key )
// if cache.table.dbg_name != "" {
// logf("%v: Refreshed %v", cache.table.dbg_name, key)
// }
pool_list_erase( & cache.key_queue, link.ptr )
pool_list_push_front( & cache.key_queue, key )
link.ptr = cache.key_queue.front

View File

@ -15,6 +15,7 @@ Changes:
*/
package VEFontCache
import "base:runtime"
import "core:math"
import "core:mem"
@ -625,7 +626,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
next_evict_codepoint := LRU_get_next_evicted( & region.state )
assert( next_evict_codepoint != 0xFFFFFFFFFFFFFFFF )
atlas_index = LRU_peek( & region.state, next_evict_codepoint )
atlas_index = LRU_peek( & region.state, next_evict_codepoint, must_find = true )
assert( atlas_index != -1 )
evicted := LRU_put( & region.state, lru_code, atlas_index )
@ -763,16 +764,17 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -
if shape_cache_idx == -1
{
if shape_cache.next_cache_id < i32(state.capacity) {
shape_cache_idx = shape_cache.next_cache_id
LRU_put( state, hash, shape_cache_idx )
shape_cache_idx = shape_cache.next_cache_id
shape_cache.next_cache_id += 1
evicted := LRU_put( state, hash, shape_cache_idx )
assert( evicted == hash )
}
else
{
next_evict_idx := LRU_get_next_evicted( state )
assert( next_evict_idx != 0xFFFFFFFFFFFFFFFF )
shape_cache_idx = LRU_peek( state, next_evict_idx )
shape_cache_idx = LRU_peek( state, next_evict_idx, must_find = true )
assert( shape_cache_idx != - 1 )
LRU_put( state, hash, shape_cache_idx )

View File

@ -8,6 +8,8 @@ import "core:mem"
Kilobyte :: mem.Kilobyte
slice_ptr :: mem.slice_ptr
Arena :: mem.Arena
arena_allocator :: mem.arena_allocator
@ -46,6 +48,7 @@ hmap_chained_destroy :: grime.hmap_chained_destroy
hmap_chained_init :: grime.hmap_chained_init
hmap_chained_get :: grime.hmap_chained_get
hmap_chained_remove :: grime.hmap_chained_remove
hmap_chained_reload :: grime.hmap_chained_reload
hmap_chained_set :: grime.hmap_chained_set
hmap_closest_prime :: grime.hmap_closest_prime