From cdd9c778929293f64147f72e5370a0cfde512e69 Mon Sep 17 00:00:00 2001 From: Richard Thier Date: Mon, 21 Oct 2024 12:53:15 +0200 Subject: [PATCH] fixed missing outer loop for not finding element at first tipp + started refactor --- simap.h | 137 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 60 deletions(-) diff --git a/simap.h b/simap.h index 1f07449..53f5797 100644 --- a/simap.h +++ b/simap.h @@ -27,18 +27,22 @@ #endif typedef uint64_t auint64 __attribute__ ((__aligned__(8))); +/** The first 8 characters are stored as uint64_t for fast checks */ +union simap_c64 { + char str8[8]; + auint64 u64; +}; +typedef union simap_char64 simap_char64; + +/** This is to ensure 8byte storage of pointers (with possible padding) */ +union simap_ptr64 { + void *ptr; + auint64 u64; +}; +typedef union simap_ptr64 simap_ptr64; + /** - * A "peasantly" map data structure backed by arena.h - basically a toy data structure... - * - * This is very simple, no trees, no hashes, just (hopefully) autovectorized linear lookup. - * Inserting NULLs to keys happens through tombstoning unless erase happens and we never - * shrink memory so please do not add a lot of things then remove a lot of things. - * - * We also only do heuristics against data being the key so its not "sure" and can fail... - * - * XXX: So beware that this can FAIL just "statistically most often works"! - * - * The per-elem memory layout after at *base is as follows: + * The per-element storage layout * * 8 byte: * - void* value; @@ -56,13 +60,36 @@ typedef uint64_t auint64 __attribute__ ((__aligned__(8))); * with few character names zero-padded in the search term parameter * and if you want check extra validity by jumping back&forth in it. */ +struct elem_prefix { + /** The value (ptr) */ + simap_ptr64 value; + + /** Previous element index from base (full offset) */ + uint32_t previndex; + /** Next element index from base (full offset) */ + uint32_t nextindex; + + /** The prefix of the key */ + simap_c64 key_prefix; +}; +typedef struct elem_prefix elem_prefix; + +/** + * A "peasantly" map data structure backed by arena.h - basically a toy data structure... + * + * This is very simple, no trees, no hashes, just (hopefully) autovectorized linear lookup. + * Inserting NULLs to keys happens through tombstoning unless erase happens and we never + * shrink memory so please do not add a lot of things then remove a lot of things. + * + * In AVX2 mode, we do heuristics against data being the key so its not "sure" and can fail... + * XXX: So beware that this CAN FAIL for AVX2 build flags just "statistically most often works"! + */ struct simap_instance { arena a; uint32_t end; uint32_t prev_usage_end; /* previous usage_end or -1 if no previous exists... in bytes!!! */ uint32_t usage_end; /* in bytes!!! */ - /** see doc comment for layout and why uint64_t* is the type */ - auint64 *base; + elem_prefix *base; }; typedef struct simap_instance simap_instance; @@ -72,27 +99,13 @@ static inline simap_instance simap_create() { ret.end = 0; ret.prev_usage_end = (uint32_t) -1; ret.usage_end = 0; - ret.base = ((auint64*) aralloc(&(ret.a), sizeof(auint64), sizeof(auint64), 1)) /* addr divisible by 8 */ - + 1; /* First really addressible thing */ + ret.base = (elem_prefix*)(((auint64*) aralloc(&(ret.a), sizeof(auint64), sizeof(auint64), 1)) /* addr divisible by 8 */ + + 1); /* First really addressible thing */ return ret; } static inline void* simap(void *amap_instance, AMAP_OP op, const char *key, void *ptr); -/** The first 8 characters are stored as uint64_t for fast checks */ -union simap_c64 { - char str8[8]; - auint64 u64; -}; -typedef union simap_char64 simap_char64; - -/** This is to ensure 8byte storage of pointers (with possible padding) */ -union simap_ptr64 { - void *ptr; - auint64 u64; -}; -typedef union simap_ptr64 simap_ptr64; - // TODO: We can possibly hand-optimise this with intrinsics maybe - but I hope autovectorization (does not seem to happen???) static inline SM_ALWAYS_INLINE auint64 *make_tipp(auint64 *tip, auint64 prefix, auint64 *end) { #pragma GCC unroll 4 @@ -113,45 +126,49 @@ static inline simap_ptr64 *simap_search_internal(simap_instance *map, const char const char *keyremains = key + prefixlen; /* Lookup prefix (fast-key) - hopefully this gets vectorized (should be)!!! */ - auint64 *base = map->base; + auint64 *base = (auint64 *) (map->base); auint64 *end = (auint64 *)((uint8_t *)base + (map->usage_end)); - auint64 *tipp = make_tipp(map->base, prefix.u64, end); - while(tipp < end) { - /* Fast lookup, because found prefix */ - assert((*tipp == prefix.u64)); + auint64 *tipp = NULL; + auint64 *nutipp = make_tipp(base, prefix.u64, end); + while(tipp != nutipp) { + tipp = nutipp; + while(tipp < end) { + /* Fast lookup, because found prefix */ + assert((*tipp == prefix.u64)); - /* First check the remains of the string (only if needed) */ - if(!is_smallkey) { - char *tippremains = (char *)((uint8_t *)tipp + sizeof(uint64_t)); - if(strcmp(keyremains, tippremains) != 0) { + /* First check the remains of the string (only if needed) */ + if(!is_smallkey) { + char *tippremains = (char *)((uint8_t *)tipp + sizeof(uint64_t)); + if(strcmp(keyremains, tippremains) != 0) { + continue; + } + } + + simap_ptr64 *ptr = (simap_ptr64 *)((uint8_t *) (tipp - 2)); + + /* Check back & forth (jump validation) */ + uint32_t previ = *((uint32_t *)(tipp - 1)); + if(previ == (uint32_t) -1) { + /* Expect it be good if it was first insert ever? Statistically rare to be not like it */ + return ptr; + } + uint32_t prevnexi = *(uint32_t *)(((uint8_t *)base) + previ + + sizeof(simap_ptr64) + + sizeof(uint32_t)); + + auint64 *retipp = (auint64 *)(((uint8_t *)base + prevnexi) + + sizeof(simap_ptr64) + sizeof(uint32_t) + + + sizeof(uint32_t)); + + if(retipp != tipp) { continue; } - } - simap_ptr64 *ptr = (simap_ptr64 *)((uint8_t *) (tipp - 2)); - - /* Check back & forth (jump validation) */ - uint32_t previ = *((uint32_t *)(tipp - 1)); - if(previ == (uint32_t) -1) { - /* Expect it be good if it was first insert ever? Statistically rare to be not like it */ + /* Can have the (statistically checked) pointer */ return ptr; } - uint32_t prevnexi = *(uint32_t *)(((uint8_t *)base) + previ - + sizeof(simap_ptr64) - + sizeof(uint32_t)); - - auint64 *retipp = (auint64 *)(((uint8_t *)base + prevnexi) - + sizeof(simap_ptr64) + sizeof(uint32_t) + - + sizeof(uint32_t)); - - if(retipp != tipp) { - continue; - } - - /* Can have the (statistically checked) pointer */ - return ptr; + nutipp = make_tipp((auint64 *) base, prefix.u64, end); } - tipp = make_tipp(map->base, prefix.u64, end); /* Haven't found anything */ return NULL;