From d2192039395f86e0522931e4e631c8feb4a95dad Mon Sep 17 00:00:00 2001 From: Richard Thier Date: Wed, 23 Oct 2024 00:27:46 +0200 Subject: [PATCH] simd multimap operations - first version, seem compatible with non-multi for now --- simd_map.h | 126 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 22 deletions(-) diff --git a/simd_map.h b/simd_map.h index 62dc785..804a455 100644 --- a/simd_map.h +++ b/simd_map.h @@ -71,12 +71,22 @@ static inline SM_ALWAYS_INLINE char simd_map_free(simd_map *map) { /** * Returns if this key is stored in the given map LANE or not - returns NULL if not found. * + * Rem.: The lane_begin and lane_next_begin parameters are used for reentrant multisearch. + * * @param map_lane The lane to find in. * @param key The key to search for. - * @param lane_modulo When non-zero, the lane only searched by this many elements. Zero means all elements. (mod lane length) + * @param lane_modulo When non-zero, the lane only searched until this index. Zero means all remaining elements. (mod lane length) + * @param lane_begin The lane is searched from this location(find_all). If full lane / lane prefix is needed, this should be 0. + * @param lane_next_begin This pointer will be filled on non-NULL retvals with the incremented in-lane index (with % modulus). * @returns NULL when not found, otherwise pointer to the stored value for the key. */ -static inline SM_ALWAYS_INLINE uint32_t *simd_map_lane_find(simd_map_lane *map_lane, uint32_t key, int lane_modulo) { +static inline SM_ALWAYS_INLINE uint32_t *simd_map_lane_find( + simd_map_lane *map_lane, + uint32_t key, + int lane_modulo, + int lane_begin, + int *lane_next_begin) { + uint32_t *keys = map_lane->keys; uint32_t *values = map_lane->values; @@ -90,18 +100,22 @@ static inline SM_ALWAYS_INLINE uint32_t *simd_map_lane_find(simd_map_lane *map_l /* Regular integer code - should have good ILP and cache locality patterns anyways */ if(lane_modulo == 0) { /** Pretty hopeful this can get more easily unrolled / autovectorized */ - for(int i = 0; i < SM_LANE_SPAN; ++i) { + for(int i = lane_begin; i < SM_LANE_SPAN; ++i) { if(SM_UNLIKELY(keys[i] == key)) { - return &values[i]; + uint32_t *ptr = &values[i]; + *lane_next_begin = (i + 1) % SM_LANE_SPAN; + return ptr; } } return NULL; } else { non_simd_modulo: - for(int i = 0; i < lane_modulo; ++i) { + for(int i = lane_begin; i < lane_modulo; ++i) { if(SM_UNLIKELY(keys[i] == key)) { - return &values[i]; + uint32_t *ptr = &values[i]; + *lane_next_begin = (i + 1) % SM_LANE_SPAN; + return ptr; } } @@ -109,31 +123,99 @@ non_simd_modulo: } } -// TODO: Implement find_all(..) and delegate to it in find(..) +/** The result of the find_all(..) operation */ +struct simd_map_find_res { + /** The found location - or NULL when the key was not found */ + uint32_t *value_location; + /** Meta-data for continuation of the search */ + uint32_t lane_next; + /** Meta-data for continuation of the search */ + int lane_next_begin; +}; +typedef struct simd_map_find_res simd_map_find_res; -/** Returns if this key is stored in the map or not - returns NULL if does not exists. */ -static inline uint32_t *simd_map_find(simd_map *map, uint32_t key) { - /* Do not process last element because of last incomplete lane */ - if(map->usage_end > 0) for(uint32_t i = 0; i < map->usage_end - 1; ++i) { - uint32_t *found = simd_map_lane_find(&(map->lanes[i]), key, 0); - if(found) return found; +/** Create the value for starting a find_all call */ +static inline simd_map_find_res simd_map_find_all_begin() { + simd_map_find_res ret; + ret.value_location = NULL; + ret.lane_next = 0; + ret.lane_next_begin = 0; + return ret; +} + +/** + * Useful for multimap-like operations to find multiple mappings for the same key. + * + * @param map The map + * @param key The key to search + * @param prev The previous result - or simd_map_find_all_begin() to find the first / start lookup! + * @returns The found pointer / location (if any) and if location was non-NULL meta-data so we can search further same-keys! + */ +static inline SM_ALWAYS_INLINE simd_map_find_res simd_map_find_all(simd_map *map, uint32_t key, simd_map_find_res prev) { + simd_map_find_res ret; + + /* Process most lanes */ + /* Do not process last element (-1) because of last incomplete lane */ + if(map->usage_end > 0) for(uint32_t i = prev.lane_next; i < map->usage_end - 1; ++i) { + uint32_t *found = simd_map_lane_find( + &(map->lanes[i]), + key, + 0, + prev.lane_next_begin, + &(ret.lane_next_begin)); /* XXX: Fills part of retval! */ + + /* Needed so only the currently found are ignored in find_all(..) */ + prev.lane_next_begin = 0; + + if(found) { + ret.value_location = found; + ret.lane_next = i + (ret.lane_next_begin != 0); + return ret; + } } /* Process last lane - with a modulo lane */ - if(map->usage_end > 0) { + if((map->usage_end > 0) && (prev.lane_next < map->usage_end)) { uint32_t *found = simd_map_lane_find( - &(map->lanes[map->usage_end - 1]), - key, - map->lane_modulo); - if(found) return found; + &(map->lanes[map->usage_end - 1]), + key, + map->lane_modulo, + prev.lane_next_begin, + &(ret.lane_next_begin)); /* XXX: Fills part of retval! */ + + /* Needed so only the currently found are ignored in find_all(..) */ + prev.lane_next_begin = 0; + + if(found) { + ret.value_location = found; + ret.lane_next = (map->usage_end - 1) + (ret.lane_next_begin != 0); + return ret; + } } /* Not found */ - return NULL; + ret = simd_map_find_all_begin(); + return ret; } -/** Useful if you know the key have never been before added (faster)! Returns 0 on errors, otherwise 1. */ -static inline SM_ALWAYS_INLINE char simd_map_force_insert(simd_map *map, uint32_t key, uint32_t value) { +/** Returns if this key is stored in the map or not - returns NULL if does not exists. */ +static inline uint32_t *simd_map_find(simd_map *map, uint32_t key) { + simd_map_find_res begin = simd_map_find_all_begin(); + simd_map_find_res fires = simd_map_find_all(map, key, begin); + return fires.value_location; +} + +/** + * Insert without checking that the value have been already added or not. + * + * Useful for multimap operation or if you know the key have never been before added (faster)! + * + * @param map The map + * @param key The key to insert + * @param value The value for this key to insert + * @returns 0 on errors, otherwise 1. + */ +static inline SM_ALWAYS_INLINE char simd_map_multi_insert(simd_map *map, uint32_t key, uint32_t value) { /* Handle storage growth needs. */ uint32_t storage_needed = (map->lane_modulo == 0) ? 1 : 0; if(SM_UNLIKELY(map->end - map->usage_end < storage_needed)) { @@ -171,7 +253,7 @@ static inline SM_ALWAYS_INLINE char simd_map_force_insert(simd_map *map, uint32_ static inline char simd_map_set(simd_map *map, uint32_t key, uint32_t value) { uint32_t *found = simd_map_find(map, key); if(!found) { - return simd_map_force_insert(map, key, value); + return simd_map_multi_insert(map, key, value); } else { /* Overwrite already existing mapping */ *found = value;