Compare commits
No commits in common. "418c8d289c377928840969bf11cb8e69e2159234" and "4ade45a655c622f60f0073fa9ca0011dda1cc21e" have entirely different histories.
418c8d289c
...
4ade45a655
16
main.cpp
16
main.cpp
@ -105,22 +105,6 @@ void test_basics(amap mapdo, void *map) {
|
|||||||
assert(NULL != (iptr = (int *)mapdo(map, AMAP_GET, "meaningless1", NULL)));
|
assert(NULL != (iptr = (int *)mapdo(map, AMAP_GET, "meaningless1", NULL)));
|
||||||
assert(*iptr == 42);
|
assert(*iptr == 42);
|
||||||
assert(iptr == &i);
|
assert(iptr == &i);
|
||||||
|
|
||||||
/* Check the case where we have same 8-long prefix for multiple and they should be different */
|
|
||||||
int long_1 = 1;
|
|
||||||
int long_2 = 2;
|
|
||||||
int long_3 = 3;
|
|
||||||
|
|
||||||
assert(NULL != mapdo(map, AMAP_SET, "very_long_test_key_1", &long_1));
|
|
||||||
assert(NULL != mapdo(map, AMAP_SET, "very_long_test_key_2", &long_2));
|
|
||||||
assert(NULL != mapdo(map, AMAP_SET, "very_long_test_key_3", &long_3));
|
|
||||||
|
|
||||||
assert(NULL != (iptr = (int *)mapdo(map, AMAP_GET, "very_long_test_key_1", NULL)));
|
|
||||||
assert(*iptr == 1);
|
|
||||||
assert(NULL != (iptr = (int *)mapdo(map, AMAP_GET, "very_long_test_key_2", NULL)));
|
|
||||||
assert(*iptr == 2);
|
|
||||||
assert(NULL != (iptr = (int *)mapdo(map, AMAP_GET, "very_long_test_key_3", NULL)));
|
|
||||||
assert(*iptr == 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
83
simap.h
83
simap.h
@ -27,22 +27,18 @@
|
|||||||
#endif
|
#endif
|
||||||
typedef uint64_t auint64 __attribute__ ((__aligned__(8)));
|
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The per-element storage layout
|
* 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:
|
||||||
*
|
*
|
||||||
* 8 byte:
|
* 8 byte:
|
||||||
* - void* value;
|
* - void* value;
|
||||||
@ -60,36 +56,13 @@ typedef union simap_ptr64 simap_ptr64;
|
|||||||
* with few character names zero-padded in the search term parameter
|
* with few character names zero-padded in the search term parameter
|
||||||
* and if you want check extra validity by jumping back&forth in it.
|
* 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 {
|
struct simap_instance {
|
||||||
arena a;
|
arena a;
|
||||||
uint32_t end;
|
uint32_t end;
|
||||||
uint32_t prev_usage_end; /* previous usage_end or -1 if no previous exists... in bytes!!! */
|
uint32_t prev_usage_end; /* previous usage_end or -1 if no previous exists... in bytes!!! */
|
||||||
uint32_t usage_end; /* in bytes!!! */
|
uint32_t usage_end; /* in bytes!!! */
|
||||||
elem_prefix *base;
|
/** see doc comment for layout and why uint64_t* is the type */
|
||||||
|
auint64 *base;
|
||||||
};
|
};
|
||||||
typedef struct simap_instance simap_instance;
|
typedef struct simap_instance simap_instance;
|
||||||
|
|
||||||
@ -99,17 +72,31 @@ static inline simap_instance simap_create() {
|
|||||||
ret.end = 0;
|
ret.end = 0;
|
||||||
ret.prev_usage_end = (uint32_t) -1;
|
ret.prev_usage_end = (uint32_t) -1;
|
||||||
ret.usage_end = 0;
|
ret.usage_end = 0;
|
||||||
ret.base = (elem_prefix*)(((auint64*) aralloc(&(ret.a), sizeof(auint64), sizeof(auint64), 1)) /* addr divisible by 8 */
|
ret.base = ((auint64*) aralloc(&(ret.a), sizeof(auint64), sizeof(auint64), 1)) /* addr divisible by 8 */
|
||||||
+ 1); /* First really addressible thing */
|
+ 1; /* First really addressible thing */
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void* simap(void *amap_instance, AMAP_OP op, const char *key, void *ptr);
|
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???)
|
// 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) {
|
static inline SM_ALWAYS_INLINE auint64 *make_tipp(auint64 *tip, auint64 prefix, auint64 *end) {
|
||||||
#pragma GCC unroll 4
|
#pragma GCC unroll 4
|
||||||
while((++tip < end) && (*tip != prefix));
|
while((tip < end) && (*tip != prefix)) ++tip;
|
||||||
return tip;
|
return tip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,19 +113,17 @@ static inline simap_ptr64 *simap_search_internal(simap_instance *map, const char
|
|||||||
const char *keyremains = key + prefixlen;
|
const char *keyremains = key + prefixlen;
|
||||||
|
|
||||||
/* Lookup prefix (fast-key) - hopefully this gets vectorized (should be)!!! */
|
/* Lookup prefix (fast-key) - hopefully this gets vectorized (should be)!!! */
|
||||||
auint64 *base = (auint64 *) (map->base);
|
auint64 *base = map->base;
|
||||||
auint64 *end = (auint64 *)((uint8_t *)base + (map->usage_end));
|
auint64 *end = (auint64 *)((uint8_t *)base + (map->usage_end));
|
||||||
auint64 *tipp = make_tipp(base, prefix.u64, end);
|
auint64 *tipp = make_tipp(map->base, prefix.u64, end);
|
||||||
while(tipp < end) {
|
while(tipp < end) {
|
||||||
|
/* Fast lookup, because found prefix */
|
||||||
/* Need detailed lookup, because found the prefix */
|
|
||||||
assert((*tipp == prefix.u64));
|
assert((*tipp == prefix.u64));
|
||||||
|
|
||||||
/* First check the remains of the string (only if needed) */
|
/* First check the remains of the string (only if needed) */
|
||||||
if(!is_smallkey) {
|
if(!is_smallkey) {
|
||||||
char *tippremains = (char *)((uint8_t *)tipp + sizeof(uint64_t));
|
char *tippremains = (char *)((uint8_t *)tipp + sizeof(uint64_t));
|
||||||
if(strcmp(keyremains, tippremains) != 0) {
|
if(strcmp(keyremains, tippremains) != 0) {
|
||||||
tipp = make_tipp(tipp, prefix.u64, end);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,13 +145,13 @@ static inline simap_ptr64 *simap_search_internal(simap_instance *map, const char
|
|||||||
+ sizeof(uint32_t));
|
+ sizeof(uint32_t));
|
||||||
|
|
||||||
if(retipp != tipp) {
|
if(retipp != tipp) {
|
||||||
tipp = make_tipp(tipp, prefix.u64, end);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Can have the (statistically checked) pointer */
|
/* Can have the (statistically checked) pointer */
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
tipp = make_tipp(map->base, prefix.u64, end);
|
||||||
|
|
||||||
/* Haven't found anything */
|
/* Haven't found anything */
|
||||||
return NULL;
|
return NULL;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user