diff options
| author | Tyge Løvset <[email protected]> | 2023-04-18 17:48:47 +0200 |
|---|---|---|
| committer | Tyge Løvset <[email protected]> | 2023-04-18 17:48:47 +0200 |
| commit | a913490c248f6cfdca3481d81d6d344f4d066cb9 (patch) | |
| tree | b805826d496a8d5c8a449731b6c8bc3b1d056979 | |
| parent | 462adebf55c77697fbb379a62941c00b876c3f6a (diff) | |
| download | STC-modified-a913490c248f6cfdca3481d81d6d344f4d066cb9.tar.gz STC-modified-a913490c248f6cfdca3481d81d6d344f4d066cb9.zip | |
Removed unneeded custom size type in maps and bits.h. Prepared for possible robin-hood impl.
Improved sso_bench.c testing string hash - twice as fast as m.ankeln robin impl !?.
| -rw-r--r-- | docs/cmap_api.md | 2 | ||||
| -rw-r--r-- | docs/cset_api.md | 2 | ||||
| -rw-r--r-- | docs/csmap_api.md | 1 | ||||
| -rw-r--r-- | docs/csset_api.md | 1 | ||||
| -rw-r--r-- | include/stc/cbits.h | 70 | ||||
| -rw-r--r-- | include/stc/ccommon.h | 6 | ||||
| -rw-r--r-- | include/stc/cmap.h | 77 | ||||
| -rw-r--r-- | include/stc/csmap.h | 79 | ||||
| -rw-r--r-- | include/stc/forward.h | 23 | ||||
| -rw-r--r-- | include/stc/priv/template.h | 4 | ||||
| -rw-r--r-- | include/stc/priv/template2.h | 1 | ||||
| -rw-r--r-- | misc/benchmarks/external/ankerl/unordered_dense.h | 414 | ||||
| -rw-r--r-- | misc/benchmarks/external/emhash/hash_table7.hpp | 44 | ||||
| -rw-r--r-- | misc/benchmarks/shootout_hashmaps.cpp | 2 | ||||
| -rw-r--r-- | misc/benchmarks/various/sso_bench.cpp | 134 |
15 files changed, 600 insertions, 260 deletions
diff --git a/docs/cmap_api.md b/docs/cmap_api.md index d2a94ee8..94f1c54e 100644 --- a/docs/cmap_api.md +++ b/docs/cmap_api.md @@ -36,7 +36,7 @@ See the c++ class [std::unordered_map](https://en.cppreference.com/w/cpp/contain #define i_valto // convertion func i_val* => i_valraw #define i_tag // alternative typename: cmap_{i_tag}. i_tag defaults to i_val -#define i_ssize // internal; default int32_t. If defined, table expand 2x (else 1.5x) +#define i_expandby // default 1. If 2, table expand 2x (else 1.5x) #include <stc/cmap.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/cset_api.md b/docs/cset_api.md index 026d7462..b9e8ae99 100644 --- a/docs/cset_api.md +++ b/docs/cset_api.md @@ -19,7 +19,7 @@ A **cset** is an associative container that contains a set of unique objects of #define i_keyto // convertion func i_key* => i_keyraw - defaults to plain copy #define i_tag // alternative typename: cmap_{i_tag}. i_tag defaults to i_val -#define i_ssize // default int32_t. If defined, table expand 2x (else 1.5x) +#define i_expandby // default 1. If 2, table expand 2x (else 1.5x) #include <stc/cset.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/csmap_api.md b/docs/csmap_api.md index 93faa4f9..3e643cee 100644 --- a/docs/csmap_api.md +++ b/docs/csmap_api.md @@ -33,7 +33,6 @@ See the c++ class [std::map](https://en.cppreference.com/w/cpp/container/map) fo #define i_valto // convertion func i_val* => i_valraw #define i_tag // alternative typename: csmap_{i_tag}. i_tag defaults to i_val -#define i_ssize // internal size rep. defaults to int32_t #include <stc/csmap.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/csset_api.md b/docs/csset_api.md index e83ab857..d095696c 100644 --- a/docs/csset_api.md +++ b/docs/csset_api.md @@ -19,7 +19,6 @@ See the c++ class [std::set](https://en.cppreference.com/w/cpp/container/set) fo #define i_keyto // convertion func i_key* => i_keyraw - defaults to plain copy #define i_tag // alternative typename: csset_{i_tag}. i_tag defaults to i_val -#define i_ssize // defaults to int32_t #include <stc/csset.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/include/stc/cbits.h b/include/stc/cbits.h index 826df847..19e281a8 100644 --- a/include/stc/cbits.h +++ b/include/stc/cbits.h @@ -54,11 +54,8 @@ int main() { #include <stdlib.h> #include <string.h> -#ifndef i_ssize -#define i_ssize intptr_t -#endif #define _cbits_bit(i) ((uint64_t)1 << ((i) & 63)) -#define _cbits_words(n) (i_ssize)(((n) + 63)>>6) +#define _cbits_words(n) (int64_t)(((n) + 63)>>6) #define _cbits_bytes(n) (_cbits_words(n) * c_sizeof(uint64_t)) #if defined(__GNUC__) @@ -80,23 +77,23 @@ int main() { } #endif -STC_INLINE i_ssize _cbits_count(const uint64_t* set, const i_ssize sz) { - const i_ssize n = sz>>6; - i_ssize count = 0; - for (i_ssize i = 0; i < n; ++i) +STC_INLINE int64_t _cbits_count(const uint64_t* set, const int64_t sz) { + const int64_t n = sz>>6; + int64_t count = 0; + for (int64_t i = 0; i < n; ++i) count += cpopcount64(set[i]); if (sz & 63) count += cpopcount64(set[n] & (_cbits_bit(sz) - 1)); return count; } -STC_INLINE char* _cbits_to_str(const uint64_t* set, const i_ssize sz, - char* out, i_ssize start, i_ssize stop) { +STC_INLINE char* _cbits_to_str(const uint64_t* set, const int64_t sz, + char* out, int64_t start, int64_t stop) { if (stop > sz) stop = sz; assert(start <= stop); c_memset(out, '0', stop - start); - for (i_ssize i = start; i < stop; ++i) + for (int64_t i = start; i < stop; ++i) if ((set[i>>6] & _cbits_bit(i)) != 0) out[i - start] = '1'; out[stop - start] = '\0'; @@ -104,8 +101,8 @@ STC_INLINE char* _cbits_to_str(const uint64_t* set, const i_ssize sz, } #define _cbits_OPR(OPR, VAL) \ - const i_ssize n = sz>>6; \ - for (i_ssize i = 0; i < n; ++i) \ + const int64_t n = sz>>6; \ + for (int64_t i = 0; i < n; ++i) \ if ((set[i] OPR other[i]) != VAL) \ return false; \ if (!(sz & 63)) \ @@ -113,10 +110,10 @@ STC_INLINE char* _cbits_to_str(const uint64_t* set, const i_ssize sz, const uint64_t i = (uint64_t)n, m = _cbits_bit(sz) - 1; \ return ((set[i] OPR other[i]) & m) == (VAL & m) -STC_INLINE bool _cbits_subset_of(const uint64_t* set, const uint64_t* other, const i_ssize sz) +STC_INLINE bool _cbits_subset_of(const uint64_t* set, const uint64_t* other, const int64_t sz) { _cbits_OPR(|, set[i]); } -STC_INLINE bool _cbits_disjoint(const uint64_t* set, const uint64_t* other, const i_ssize sz) +STC_INLINE bool _cbits_disjoint(const uint64_t* set, const uint64_t* other, const int64_t sz) { _cbits_OPR(&, 0); } #endif // CBITS_H_INCLUDED @@ -128,12 +125,12 @@ STC_INLINE bool _cbits_disjoint(const uint64_t* set, const uint64_t* other, cons #define _i_assert(x) assert(x) #define i_type cbits -struct { uint64_t *data64; i_ssize _size; } typedef i_type; +struct { uint64_t *data64; int64_t _size; } typedef i_type; STC_INLINE cbits cbits_init(void) { return c_LITERAL(cbits){NULL}; } STC_INLINE void cbits_create(cbits* self) { self->data64 = NULL; self->_size = 0; } STC_INLINE void cbits_drop(cbits* self) { c_free(self->data64); } -STC_INLINE i_ssize cbits_size(const cbits* self) { return self->_size; } +STC_INLINE int64_t cbits_size(const cbits* self) { return self->_size; } STC_INLINE cbits* cbits_take(cbits* self, cbits other) { if (self->data64 != other.data64) { @@ -144,7 +141,7 @@ STC_INLINE cbits* cbits_take(cbits* self, cbits other) { } STC_INLINE cbits cbits_clone(cbits other) { - const i_ssize bytes = _cbits_bytes(other._size); + const int64_t bytes = _cbits_bytes(other._size); cbits set = {(uint64_t *)c_memcpy(c_malloc(bytes), other.data64, bytes), other._size}; return set; } @@ -158,8 +155,8 @@ STC_INLINE cbits* cbits_copy(cbits* self, const cbits* other) { return self; } -STC_INLINE void cbits_resize(cbits* self, const i_ssize size, const bool value) { - const i_ssize new_n = _cbits_words(size), osize = self->_size, old_n = _cbits_words(osize); +STC_INLINE void cbits_resize(cbits* self, const int64_t size, const bool value) { + const int64_t new_n = _cbits_words(size), osize = self->_size, old_n = _cbits_words(osize); self->data64 = (uint64_t *)c_realloc(self->data64, new_n*8); self->_size = size; if (new_n >= old_n) { @@ -181,13 +178,13 @@ STC_INLINE cbits cbits_move(cbits* self) { return tmp; } -STC_INLINE cbits cbits_with_size(const i_ssize size, const bool value) { +STC_INLINE cbits cbits_with_size(const int64_t size, const bool value) { cbits set = {(uint64_t *)c_malloc(_cbits_bytes(size)), size}; cbits_set_all(&set, value); return set; } -STC_INLINE cbits cbits_with_pattern(const i_ssize size, const uint64_t pattern) { +STC_INLINE cbits cbits_with_pattern(const int64_t size, const uint64_t pattern) { cbits set = {(uint64_t *)c_malloc(_cbits_bytes(size)), size}; cbits_set_pattern(&set, pattern); return set; @@ -205,7 +202,7 @@ struct { uint64_t data64[(i_capacity - 1)/64 + 1]; } typedef i_type; STC_INLINE i_type _i_memb(_init)(void) { return c_LITERAL(i_type){0}; } STC_INLINE void _i_memb(_create)(i_type* self) {} STC_INLINE void _i_memb(_drop)(i_type* self) {} -STC_INLINE i_ssize _i_memb(_size)(const i_type* self) { return i_capacity; } +STC_INLINE int64_t _i_memb(_size)(const i_type* self) { return i_capacity; } STC_INLINE i_type _i_memb(_move)(i_type* self) { return *self; } STC_INLINE i_type* _i_memb(_take)(i_type* self, i_type other) @@ -220,13 +217,13 @@ STC_INLINE i_type* _i_memb(_copy)(i_type* self, const i_type* other) STC_INLINE void _i_memb(_set_all)(i_type *self, const bool value); STC_INLINE void _i_memb(_set_pattern)(i_type *self, const uint64_t pattern); -STC_INLINE i_type _i_memb(_with_size)(const i_ssize size, const bool value) { +STC_INLINE i_type _i_memb(_with_size)(const int64_t size, const bool value) { assert(size <= i_capacity); i_type set; _i_memb(_set_all)(&set, value); return set; } -STC_INLINE i_type _i_memb(_with_pattern)(const i_ssize size, const uint64_t pattern) { +STC_INLINE i_type _i_memb(_with_pattern)(const int64_t size, const uint64_t pattern) { assert(size <= i_capacity); i_type set; _i_memb(_set_pattern)(&set, pattern); return set; @@ -239,31 +236,31 @@ STC_INLINE void _i_memb(_set_all)(i_type *self, const bool value) { c_memset(self->data64, value? ~0 : 0, _cbits_bytes(_i_memb(_size)(self))); } STC_INLINE void _i_memb(_set_pattern)(i_type *self, const uint64_t pattern) { - i_ssize n = _cbits_words(_i_memb(_size)(self)); + int64_t n = _cbits_words(_i_memb(_size)(self)); while (n--) self->data64[n] = pattern; } -STC_INLINE bool _i_memb(_test)(const i_type* self, const i_ssize i) +STC_INLINE bool _i_memb(_test)(const i_type* self, const int64_t i) { return (self->data64[i>>6] & _cbits_bit(i)) != 0; } -STC_INLINE bool _i_memb(_at)(const i_type* self, const i_ssize i) +STC_INLINE bool _i_memb(_at)(const i_type* self, const int64_t i) { return (self->data64[i>>6] & _cbits_bit(i)) != 0; } -STC_INLINE void _i_memb(_set)(i_type *self, const i_ssize i) +STC_INLINE void _i_memb(_set)(i_type *self, const int64_t i) { self->data64[i>>6] |= _cbits_bit(i); } -STC_INLINE void _i_memb(_reset)(i_type *self, const i_ssize i) +STC_INLINE void _i_memb(_reset)(i_type *self, const int64_t i) { self->data64[i>>6] &= ~_cbits_bit(i); } -STC_INLINE void _i_memb(_set_value)(i_type *self, const i_ssize i, const bool b) { +STC_INLINE void _i_memb(_set_value)(i_type *self, const int64_t i, const bool b) { self->data64[i>>6] ^= ((uint64_t)-(int)b ^ self->data64[i>>6]) & _cbits_bit(i); } -STC_INLINE void _i_memb(_flip)(i_type *self, const i_ssize i) +STC_INLINE void _i_memb(_flip)(i_type *self, const int64_t i) { self->data64[i>>6] ^= _cbits_bit(i); } STC_INLINE void _i_memb(_flip_all)(i_type *self) { - i_ssize n = _cbits_words(_i_memb(_size)(self)); + int64_t n = _cbits_words(_i_memb(_size)(self)); while (n--) self->data64[n] ^= ~(uint64_t)0; } @@ -277,19 +274,19 @@ STC_INLINE i_type _i_memb(_from)(const char* str) { /* Intersection */ STC_INLINE void _i_memb(_intersect)(i_type *self, const i_type* other) { _i_assert(self->_size == other->_size); - i_ssize n = _cbits_words(_i_memb(_size)(self)); + int64_t n = _cbits_words(_i_memb(_size)(self)); while (n--) self->data64[n] &= other->data64[n]; } /* Union */ STC_INLINE void _i_memb(_union)(i_type *self, const i_type* other) { _i_assert(self->_size == other->_size); - i_ssize n = _cbits_words(_i_memb(_size)(self)); + int64_t n = _cbits_words(_i_memb(_size)(self)); while (n--) self->data64[n] |= other->data64[n]; } /* Exclusive disjunction */ STC_INLINE void _i_memb(_xor)(i_type *self, const i_type* other) { _i_assert(self->_size == other->_size); - i_ssize n = _cbits_words(_i_memb(_size)(self)); + int64_t n = _cbits_words(_i_memb(_size)(self)); while (n--) self->data64[n] ^= other->data64[n]; } @@ -312,7 +309,6 @@ STC_INLINE bool _i_memb(_disjoint)(const i_type* self, const i_type* other) { #pragma GCC diagnostic pop #endif #define CBITS_H_INCLUDED -#undef _i_size #undef _i_memb #undef _i_assert #undef i_capacity diff --git a/include/stc/ccommon.h b/include/stc/ccommon.h index bc12e642..0735e855 100644 --- a/include/stc/ccommon.h +++ b/include/stc/ccommon.h @@ -147,13 +147,13 @@ STC_INLINE uint64_t cfasthash(const void* key, intptr_t len) { case 0: return 1; } const uint8_t *x = (const uint8_t*)key; - uint64_t h = 0xcbf29ce484222325 + *x*0x811c9dc5UL, n = (uint64_t)len >> 3; + uint64_t h = *x*0x811c9dc5ULL, n = (uint64_t)len >> 3; len &= 7; while (n--) { memcpy(&u8, x, 8), x += 8; - h = (h ^ u8)*0x01000193UL; + h = (h ^ u8)*0x01000193ULL; } - while (len--) h = (h ^ *x++)*0x01000193UL; + while (len--) h = (h ^ *x++)*0x01000193ULL; return h; } diff --git a/include/stc/cmap.h b/include/stc/cmap.h index 14782b71..cfed5540 100644 --- a/include/stc/cmap.h +++ b/include/stc/cmap.h @@ -73,17 +73,12 @@ typedef struct { int64_t idx; uint8_t hx; } chash_bucket_t; #ifndef i_max_load_factor #define i_max_load_factor 0.85f #endif -#ifndef i_ssize - #define i_ssize int32_t - #define _i_size intptr_t - #define _i_expandby 1 -#else - #define _i_expandby 2 - #define _i_size i_ssize +#ifndef i_expandby + #define i_expandby 1 #endif #include "priv/template.h" #ifndef i_is_forward - _cx_deftypes(_c_chash_types, _cx_self, i_key, i_val, i_ssize, _i_MAP_ONLY, _i_SET_ONLY); + _cx_deftypes(_c_chash_types, _cx_self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY); #endif _i_MAP_ONLY( struct _cx_value { @@ -98,25 +93,25 @@ typedef _i_SET_ONLY( i_keyraw ) i_valraw second; } ) _cx_raw; -STC_API _cx_self _cx_memb(_with_capacity)(_i_size cap); +STC_API _cx_self _cx_memb(_with_capacity)(intptr_t cap); #if !defined i_no_clone STC_API _cx_self _cx_memb(_clone)(_cx_self map); #endif STC_API void _cx_memb(_drop)(_cx_self* self); STC_API void _cx_memb(_clear)(_cx_self* self); -STC_API bool _cx_memb(_reserve)(_cx_self* self, _i_size capacity); +STC_API bool _cx_memb(_reserve)(_cx_self* self, intptr_t capacity); STC_API chash_bucket_t _cx_memb(_bucket_)(const _cx_self* self, const _cx_keyraw* rkeyptr); STC_API _cx_result _cx_memb(_insert_entry_)(_cx_self* self, _cx_keyraw rkey); STC_API void _cx_memb(_erase_entry)(_cx_self* self, _cx_value* val); STC_INLINE _cx_self _cx_memb(_init)(void) { _cx_self map = {0}; return map; } -STC_INLINE void _cx_memb(_shrink_to_fit)(_cx_self* self) { _cx_memb(_reserve)(self, self->size); } +STC_INLINE void _cx_memb(_shrink_to_fit)(_cx_self* self) { _cx_memb(_reserve)(self, (intptr_t)self->size); } STC_INLINE float _cx_memb(_max_load_factor)(const _cx_self* self) { return (float)(i_max_load_factor); } STC_INLINE bool _cx_memb(_empty)(const _cx_self* map) { return !map->size; } -STC_INLINE _i_size _cx_memb(_size)(const _cx_self* map) { return map->size; } -STC_INLINE _i_size _cx_memb(_bucket_count)(_cx_self* map) { return map->bucket_count; } -STC_INLINE _i_size _cx_memb(_capacity)(const _cx_self* map) - { return (_i_size)((float)map->bucket_count * (i_max_load_factor)); } +STC_INLINE intptr_t _cx_memb(_size)(const _cx_self* map) { return (intptr_t)map->size; } +STC_INLINE intptr_t _cx_memb(_bucket_count)(_cx_self* map) { return map->bucket.count; } +STC_INLINE intptr_t _cx_memb(_capacity)(const _cx_self* map) + { return (intptr_t)((float)map->bucket.count * (i_max_load_factor)); } STC_INLINE bool _cx_memb(_contains)(const _cx_self* self, _cx_keyraw rkey) { return self->size && self->_hashx[_cx_memb(_bucket_)(self, &rkey).idx]; } @@ -197,7 +192,7 @@ _cx_memb(_push)(_cx_self* self, _cx_value _val) { return _res; } -STC_INLINE void _cx_memb(_put_n)(_cx_self* self, const _cx_raw* raw, _i_size n) { +STC_INLINE void _cx_memb(_put_n)(_cx_self* self, const _cx_raw* raw, intptr_t n) { while (n--) #if defined _i_isset && defined i_no_emplace _cx_memb(_insert)(self, *raw++); @@ -210,11 +205,11 @@ STC_INLINE void _cx_memb(_put_n)(_cx_self* self, const _cx_raw* raw, _i_size n) #endif } -STC_INLINE _cx_self _cx_memb(_from_n)(const _cx_raw* raw, _i_size n) +STC_INLINE _cx_self _cx_memb(_from_n)(const _cx_raw* raw, intptr_t n) { _cx_self cx = {0}; _cx_memb(_put_n)(&cx, raw, n); return cx; } STC_INLINE _cx_iter _cx_memb(_begin)(const _cx_self* self) { - _cx_iter it = {self->table, self->table+self->bucket_count, self->_hashx}; + _cx_iter it = {self->table, self->table+self->bucket.count, self->_hashx}; if (it._hx) while (*it._hx == 0) ++it.ref, ++it._hx; @@ -243,7 +238,7 @@ _cx_memb(_find)(const _cx_self* self, _cx_keyraw rkey) { int64_t idx; if (self->size && self->_hashx[idx = _cx_memb(_bucket_)(self, &rkey).idx]) return c_LITERAL(_cx_iter){self->table + idx, - self->table + self->bucket_count, + self->table + self->bucket.count, self->_hashx + idx}; return _cx_memb(_end)(self); } @@ -306,7 +301,7 @@ STC_INLINE uint64_t next_power_of_2(uint64_t n) { #endif // CMAP_H_INCLUDED STC_DEF _cx_self -_cx_memb(_with_capacity)(const _i_size cap) { +_cx_memb(_with_capacity)(const intptr_t cap) { _cx_self h = {0}; _cx_memb(_reserve)(&h, cap); return h; @@ -315,7 +310,7 @@ _cx_memb(_with_capacity)(const _i_size cap) { STC_INLINE void _cx_memb(_wipe_)(_cx_self* self) { if (self->size == 0) return; - _cx_value* e = self->table, *end = e + self->bucket_count; + _cx_value* e = self->table, *end = e + self->bucket.count; uint8_t *hx = self->_hashx; for (; e != end; ++e) if (*hx++) @@ -331,7 +326,7 @@ STC_DEF void _cx_memb(_drop)(_cx_self* self) { STC_DEF void _cx_memb(_clear)(_cx_self* self) { _cx_memb(_wipe_)(self); self->size = 0; - c_memset(self->_hashx, 0, self->bucket_count); + c_memset(self->_hashx, 0, self->bucket.count); } #ifndef _i_isset @@ -366,8 +361,8 @@ STC_DEF void _cx_memb(_clear)(_cx_self* self) { STC_DEF chash_bucket_t _cx_memb(_bucket_)(const _cx_self* self, const _cx_keyraw* rkeyptr) { const uint64_t _hash = i_hash(rkeyptr); - int64_t _cap = self->bucket_count; - chash_bucket_t b = {c_PASTE(fastrange_,_i_expandby)(_hash, (uint64_t)_cap), (uint8_t)(_hash | 0x80)}; + int64_t _cap = self->bucket.count; + chash_bucket_t b = {c_PASTE(fastrange_,i_expandby)(_hash, (uint64_t)_cap), (uint8_t)(_hash | 0x80)}; const uint8_t* _hx = self->_hashx; while (_hx[b.idx]) { if (_hx[b.idx] == b.hx) { @@ -384,8 +379,8 @@ _cx_memb(_bucket_)(const _cx_self* self, const _cx_keyraw* rkeyptr) { STC_DEF _cx_result _cx_memb(_insert_entry_)(_cx_self* self, _cx_keyraw rkey) { _cx_result res = {NULL}; - if (self->size + 2 > (i_ssize)((float)self->bucket_count * (i_max_load_factor))) - if (!_cx_memb(_reserve)(self, self->size*3/2)) + if (self->size + 2 > (intptr_t)((float)self->bucket.count * (i_max_load_factor))) + if (!_cx_memb(_reserve)(self, (intptr_t)(self->size*3/2))) return res; chash_bucket_t b = _cx_memb(_bucket_)(self, &rkey); @@ -401,11 +396,11 @@ _cx_memb(_insert_entry_)(_cx_self* self, _cx_keyraw rkey) { STC_DEF _cx_self _cx_memb(_clone)(_cx_self m) { if (m.table) { - _cx_value *t = (_cx_value *)i_malloc(c_sizeof(_cx_value)*m.bucket_count), - *dst = t, *m_end = m.table + m.bucket_count; - uint8_t *h = (uint8_t *)c_memcpy(i_malloc(m.bucket_count + 1), m._hashx, m.bucket_count + 1); + _cx_value *t = (_cx_value *)i_malloc(c_sizeof(_cx_value)*m.bucket.count), + *dst = t, *m_end = m.table + m.bucket.count; + uint8_t *h = (uint8_t *)c_memcpy(i_malloc(m.bucket.count + 1), m._hashx, m.bucket.count + 1); if (!(t && h)) - { i_free(t), i_free(h), t = 0, h = 0, m.bucket_count = 0; } + { i_free(t), i_free(h), t = 0, h = 0, m.bucket.count = 0; } else for (; m.table != m_end; ++m.table, ++m._hashx, ++dst) if (*m._hashx) @@ -417,27 +412,27 @@ _cx_memb(_clone)(_cx_self m) { #endif STC_DEF bool -_cx_memb(_reserve)(_cx_self* self, const _i_size newcap) { - const i_ssize _oldbuckets = self->bucket_count, _newcap = (i_ssize)newcap; +_cx_memb(_reserve)(_cx_self* self, const intptr_t newcap) { + const intptr_t _oldbuckets = self->bucket.count, _newcap = newcap; if (_newcap != self->size && _newcap <= _oldbuckets) return true; - i_ssize _nbuckets = (i_ssize)((float)_newcap / (i_max_load_factor)) + 4; - #if _i_expandby == 2 - _nbuckets = (i_ssize)next_power_of_2(_nbuckets); + uintptr_t _nbuckets = (uintptr_t)((float)_newcap / (i_max_load_factor)) + 4; + #if i_expandby == 2 + _nbuckets = (intptr_t)next_power_of_2(_nbuckets); #else _nbuckets |= 1; #endif _cx_self m = { (_cx_value *)i_malloc(c_sizeof(_cx_value)*_nbuckets), (uint8_t *)i_calloc(_nbuckets + 1, 1), - self->size, _nbuckets, + self->size, {_nbuckets & ((1ULL << 48) - 1)} }; bool ok = m.table && m._hashx; if (ok) { /* Rehash: */ m._hashx[_nbuckets] = 0xff; const _cx_value* e = self->table; const uint8_t* h = self->_hashx; - for (i_ssize i = 0; i < _oldbuckets; ++i, ++e) if (*h++) { + for (intptr_t i = 0; i < _oldbuckets; ++i, ++e) if (*h++) { _cx_keyraw r = i_keyto(_i_keyref(e)); chash_bucket_t b = _cx_memb(_bucket_)(&m, &r); m.table[b.idx] = *e; @@ -452,8 +447,8 @@ _cx_memb(_reserve)(_cx_self* self, const _i_size newcap) { STC_DEF void _cx_memb(_erase_entry)(_cx_self* self, _cx_value* _val) { - i_ssize i = (i_ssize)(_val - self->table), j = i, k; - const i_ssize _cap = self->bucket_count; + intptr_t i = (intptr_t)(_val - self->table), j = i, k; + const intptr_t _cap = self->bucket.count; _cx_value* _slot = self->table; uint8_t* _hashx = self->_hashx; _cx_memb(_value_drop)(_val); @@ -463,7 +458,7 @@ _cx_memb(_erase_entry)(_cx_self* self, _cx_value* _val) { if (! _hashx[j]) break; const _cx_keyraw _raw = i_keyto(_i_keyref(_slot + j)); - k = (i_ssize)c_PASTE(fastrange_,_i_expandby)(i_hash((&_raw)), (uint64_t)_cap); + k = (intptr_t)c_PASTE(fastrange_,i_expandby)(i_hash((&_raw)), (uint64_t)_cap); if ((j < i) ^ (k <= i) ^ (k > j)) /* is k outside (i, j]? */ _slot[i] = _slot[j], _hashx[i] = _hashx[j], i = j; } @@ -473,7 +468,7 @@ _cx_memb(_erase_entry)(_cx_self* self, _cx_value* _val) { #endif // i_implement #undef i_max_load_factor -#undef _i_size +#undef i_expandby #undef _i_isset #undef _i_ismap #undef _i_ishash diff --git a/include/stc/csmap.h b/include/stc/csmap.h index ebfe8d44..dc0387f7 100644 --- a/include/stc/csmap.h +++ b/include/stc/csmap.h @@ -70,15 +70,9 @@ int main(void) { #define _i_SET_ONLY c_false #define _i_keyref(vp) (&(vp)->first) #endif -#ifndef i_ssize - #define i_ssize int32_t - #define _i_size intptr_t -#else - #define _i_size i_ssize -#endif #include "priv/template.h" #ifndef i_is_forward - _cx_deftypes(_c_aatree_types, _cx_self, i_key, i_val, i_ssize, _i_MAP_ONLY, _i_SET_ONLY); + _cx_deftypes(_c_aatree_types, _cx_self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY); #endif _i_MAP_ONLY( struct _cx_value { @@ -86,7 +80,7 @@ _i_MAP_ONLY( struct _cx_value { _cx_mapped second; }; ) struct _cx_node { - i_ssize link[2]; + int32_t link[2]; int8_t level; _cx_value value; }; @@ -106,7 +100,7 @@ STC_API _cx_self _cx_memb(_clone)(_cx_self tree); STC_API _cx_result _cx_memb(_insert)(_cx_self* self, i_key key _i_MAP_ONLY(, i_val mapped)); STC_API _cx_result _cx_memb(_push)(_cx_self* self, _cx_value _val); STC_API void _cx_memb(_drop)(_cx_self* self); -STC_API bool _cx_memb(_reserve)(_cx_self* self, _i_size cap); +STC_API bool _cx_memb(_reserve)(_cx_self* self, intptr_t cap); STC_API _cx_value* _cx_memb(_find_it)(const _cx_self* self, _cx_keyraw rkey, _cx_iter* out); STC_API _cx_iter _cx_memb(_lower_bound)(const _cx_self* self, _cx_keyraw rkey); STC_API _cx_value* _cx_memb(_front)(const _cx_self* self); @@ -118,8 +112,8 @@ STC_API void _cx_memb(_next)(_cx_iter* it); STC_INLINE _cx_self _cx_memb(_init)(void) { _cx_self tree = {0}; return tree; } STC_INLINE bool _cx_memb(_empty)(const _cx_self* cx) { return cx->size == 0; } -STC_INLINE _i_size _cx_memb(_size)(const _cx_self* cx) { return cx->size; } -STC_INLINE _i_size _cx_memb(_capacity)(const _cx_self* cx) { return cx->cap; } +STC_INLINE intptr_t _cx_memb(_size)(const _cx_self* cx) { return cx->size; } +STC_INLINE intptr_t _cx_memb(_capacity)(const _cx_self* cx) { return cx->cap; } STC_INLINE _cx_iter _cx_memb(_find)(const _cx_self* self, _cx_keyraw rkey) { _cx_iter it; _cx_memb(_find_it)(self, rkey, &it); return it; } STC_INLINE bool _cx_memb(_contains)(const _cx_self* self, _cx_keyraw rkey) @@ -130,7 +124,7 @@ STC_INLINE _cx_value* _cx_memb(_get_mut)(_cx_self* self, _cx_keyraw rkey) { _cx_iter it; return _cx_memb(_find_it)(self, rkey, &it); } STC_INLINE _cx_self -_cx_memb(_with_capacity)(const _i_size cap) { +_cx_memb(_with_capacity)(const intptr_t cap) { _cx_self tree = _cx_memb(_init)(); _cx_memb(_reserve)(&tree, cap); return tree; @@ -226,7 +220,7 @@ _cx_memb(_eq)(const _cx_self* self, const _cx_self* other) { return true; } -STC_INLINE void _cx_memb(_put_n)(_cx_self* self, const _cx_raw* raw, _i_size n) { +STC_INLINE void _cx_memb(_put_n)(_cx_self* self, const _cx_raw* raw, intptr_t n) { while (n--) #if defined _i_isset && defined i_no_emplace _cx_memb(_insert)(self, *raw++); @@ -239,14 +233,14 @@ STC_INLINE void _cx_memb(_put_n)(_cx_self* self, const _cx_raw* raw, _i_size n) #endif } -STC_INLINE _cx_self _cx_memb(_from_n)(const _cx_raw* raw, _i_size n) +STC_INLINE _cx_self _cx_memb(_from_n)(const _cx_raw* raw, intptr_t n) { _cx_self cx = {0}; _cx_memb(_put_n)(&cx, raw, n); return cx; } /* -------------------------- IMPLEMENTATION ------------------------- */ #if defined(i_implement) STC_DEF bool -_cx_memb(_reserve)(_cx_self* self, const _i_size cap) { +_cx_memb(_reserve)(_cx_self* self, const intptr_t cap) { if (cap <= self->cap) return false; _cx_node* nodes = (_cx_node*)i_realloc(self->nodes, (cap + 1)*c_sizeof(_cx_node)); @@ -254,14 +248,14 @@ _cx_memb(_reserve)(_cx_self* self, const _i_size cap) { return false; nodes[0] = c_LITERAL(_cx_node){{0, 0}, 0}; self->nodes = nodes; - self->cap = (i_ssize)cap; + self->cap = (int32_t)cap; return true; } STC_DEF _cx_value* _cx_memb(_front)(const _cx_self* self) { _cx_node *d = self->nodes; - i_ssize tn = self->root; + int32_t tn = self->root; while (d[tn].link[0]) tn = d[tn].link[0]; return &d[tn].value; @@ -270,15 +264,15 @@ _cx_memb(_front)(const _cx_self* self) { STC_DEF _cx_value* _cx_memb(_back)(const _cx_self* self) { _cx_node *d = self->nodes; - i_ssize tn = self->root; + int32_t tn = self->root; while (d[tn].link[1]) tn = d[tn].link[1]; return &d[tn].value; } -static i_ssize +static int32_t _cx_memb(_new_node_)(_cx_self* self, int level) { - i_ssize tn; + int32_t tn; if (self->disp) { tn = self->disp; self->disp = self->nodes[tn].link[1]; @@ -346,7 +340,7 @@ _cx_memb(_push)(_cx_self* self, _cx_value _val) { STC_DEF _cx_value* _cx_memb(_find_it)(const _cx_self* self, _cx_keyraw rkey, _cx_iter* out) { - i_ssize tn = self->root; + int32_t tn = self->root; _cx_node *d = out->_d = self->nodes; out->_top = 0; while (tn) { @@ -366,7 +360,7 @@ _cx_memb(_lower_bound)(const _cx_self* self, _cx_keyraw rkey) { _cx_iter it; _cx_memb(_find_it)(self, rkey, &it); if (!it.ref && it._top) { - i_ssize tn = it._st[--it._top]; + int32_t tn = it._st[--it._top]; it._tn = it._d[tn].link[1]; it.ref = &it._d[tn].value; } @@ -375,7 +369,7 @@ _cx_memb(_lower_bound)(const _cx_self* self, _cx_keyraw rkey) { STC_DEF void _cx_memb(_next)(_cx_iter *it) { - i_ssize tn = it->_tn; + int32_t tn = it->_tn; if (it->_top || tn) { while (tn) { it->_st[it->_top++] = tn; @@ -388,10 +382,10 @@ _cx_memb(_next)(_cx_iter *it) { it->ref = NULL; } -STC_DEF i_ssize -_cx_memb(_skew_)(_cx_node *d, i_ssize tn) { +STC_DEF int32_t +_cx_memb(_skew_)(_cx_node *d, int32_t tn) { if (tn && d[d[tn].link[0]].level == d[tn].level) { - i_ssize tmp = d[tn].link[0]; + int32_t tmp = d[tn].link[0]; d[tn].link[0] = d[tmp].link[1]; d[tmp].link[1] = tn; tn = tmp; @@ -399,10 +393,10 @@ _cx_memb(_skew_)(_cx_node *d, i_ssize tn) { return tn; } -STC_DEF i_ssize -_cx_memb(_split_)(_cx_node *d, i_ssize tn) { +STC_DEF int32_t +_cx_memb(_split_)(_cx_node *d, int32_t tn) { if (d[d[d[tn].link[1]].link[1]].level == d[tn].level) { - i_ssize tmp = d[tn].link[1]; + int32_t tmp = d[tn].link[1]; d[tn].link[1] = d[tmp].link[0]; d[tmp].link[0] = tn; tn = tmp; @@ -411,9 +405,9 @@ _cx_memb(_split_)(_cx_node *d, i_ssize tn) { return tn; } -static i_ssize -_cx_memb(_insert_entry_i_)(_cx_self* self, i_ssize tn, const _cx_keyraw* rkey, _cx_result* _res) { - i_ssize up[64], tx = tn; +static int32_t +_cx_memb(_insert_entry_i_)(_cx_self* self, int32_t tn, const _cx_keyraw* rkey, _cx_result* _res) { + int32_t up[64], tx = tn; _cx_node* d = self->nodes; int c, top = 0, dir = 0; while (tx) { @@ -446,19 +440,19 @@ _cx_memb(_insert_entry_i_)(_cx_self* self, i_ssize tn, const _cx_keyraw* rkey, _ static _cx_result _cx_memb(_insert_entry_)(_cx_self* self, _cx_keyraw rkey) { _cx_result res = {NULL}; - i_ssize tn = _cx_memb(_insert_entry_i_)(self, self->root, &rkey, &res); + int32_t tn = _cx_memb(_insert_entry_i_)(self, self->root, &rkey, &res); self->root = tn; self->size += res.inserted; return res; } -static i_ssize -_cx_memb(_erase_r_)(_cx_self *self, i_ssize tn, const _cx_keyraw* rkey, int *erased) { +static int32_t +_cx_memb(_erase_r_)(_cx_self *self, int32_t tn, const _cx_keyraw* rkey, int *erased) { _cx_node *d = self->nodes; if (tn == 0) return 0; _cx_keyraw raw = i_keyto(_i_keyref(&d[tn].value)); - i_ssize tx; int c = i_cmp((&raw), rkey); + int32_t tx; int c = i_cmp((&raw), rkey); if (c != 0) d[tn].link[c < 0] = _cx_memb(_erase_r_)(self, d[tn].link[c < 0], rkey, erased); else { @@ -495,7 +489,7 @@ _cx_memb(_erase_r_)(_cx_self *self, i_ssize tn, const _cx_keyraw* rkey, int *era STC_DEF int _cx_memb(_erase)(_cx_self* self, _cx_keyraw rkey) { int erased = 0; - i_ssize root = _cx_memb(_erase_r_)(self, self->root, &rkey, &erased); + int32_t root = _cx_memb(_erase_r_)(self, self->root, &rkey, &erased); if (!erased) return 0; self->root = root; @@ -537,11 +531,11 @@ _cx_memb(_erase_range)(_cx_self* self, _cx_iter it1, _cx_iter it2) { } #if !defined i_no_clone -static i_ssize -_cx_memb(_clone_r_)(_cx_self* self, _cx_node* src, i_ssize sn) { +static int32_t +_cx_memb(_clone_r_)(_cx_self* self, _cx_node* src, int32_t sn) { if (sn == 0) return 0; - i_ssize tx, tn = _cx_memb(_new_node_)(self, src[sn].level); + int32_t tx, tn = _cx_memb(_new_node_)(self, src[sn].level); self->nodes[tn].value = _cx_memb(_value_clone)(src[sn].value); tx = _cx_memb(_clone_r_)(self, src, src[sn].link[0]); self->nodes[tn].link[0] = tx; tx = _cx_memb(_clone_r_)(self, src, src[sn].link[1]); self->nodes[tn].link[1] = tx; @@ -551,7 +545,7 @@ _cx_memb(_clone_r_)(_cx_self* self, _cx_node* src, i_ssize sn) { STC_DEF _cx_self _cx_memb(_clone)(_cx_self tree) { _cx_self clone = _cx_memb(_with_capacity)(tree.size); - i_ssize root = _cx_memb(_clone_r_)(&clone, tree.nodes, tree.root); + int32_t root = _cx_memb(_clone_r_)(&clone, tree.nodes, tree.root); clone.root = root; clone.size = tree.size; return clone; @@ -571,7 +565,7 @@ _cx_memb(_emplace)(_cx_self* self, _cx_keyraw rkey _i_MAP_ONLY(, i_valraw rmappe #endif // i_no_emplace static void -_cx_memb(_drop_r_)(_cx_node* d, i_ssize tn) { +_cx_memb(_drop_r_)(_cx_node* d, int32_t tn) { if (tn) { _cx_memb(_drop_r_)(d, d[tn].link[0]); _cx_memb(_drop_r_)(d, d[tn].link[1]); @@ -588,7 +582,6 @@ _cx_memb(_drop)(_cx_self* self) { } #endif // i_implement -#undef _i_size #undef _i_isset #undef _i_ismap #undef _i_keyref diff --git a/include/stc/forward.h b/include/stc/forward.h index 31e67e7d..e543b94a 100644 --- a/include/stc/forward.h +++ b/include/stc/forward.h @@ -29,12 +29,10 @@ #define forward_cbox(CX, VAL) _c_cbox_types(CX, VAL) #define forward_cdeq(CX, VAL) _c_cdeq_types(CX, VAL) #define forward_clist(CX, VAL) _c_clist_types(CX, VAL) -#define forward_cmap(CX, KEY, VAL) _c_chash_types(CX, KEY, VAL, int32_t, c_true, c_false) -#define forward_cmap64(CX, KEY, VAL) _c_chash_types(CX, KEY, VAL, int64_t, c_true, c_false) -#define forward_cset(CX, KEY) _c_chash_types(CX, cset, KEY, KEY, int32_t, c_false, c_true) -#define forward_cset64(CX, KEY) _c_chash_types(CX, cset, KEY, KEY, int64_t, c_false, c_true) -#define forward_csmap(CX, KEY, VAL) _c_aatree_types(CX, KEY, VAL, int32_t, c_true, c_false) -#define forward_csset(CX, KEY) _c_aatree_types(CX, KEY, KEY, int32_t, c_false, c_true) +#define forward_cmap(CX, KEY, VAL) _c_chash_types(CX, KEY, VAL, c_true, c_false) +#define forward_cset(CX, KEY) _c_chash_types(CX, cset, KEY, KEY, c_false, c_true) +#define forward_csmap(CX, KEY, VAL) _c_aatree_types(CX, KEY, VAL, c_true, c_false) +#define forward_csset(CX, KEY) _c_aatree_types(CX, KEY, KEY, c_false, c_true) #define forward_cstack(CX, VAL) _c_cstack_types(CX, VAL) #define forward_cpque(CX, VAL) _c_cpque_types(CX, VAL) #define forward_cqueue(CX, VAL) _c_cdeq_types(CX, VAL) @@ -103,10 +101,9 @@ typedef union { SELF##_node *last; \ } SELF -#define _c_chash_types(SELF, KEY, VAL, SZ, MAP_ONLY, SET_ONLY) \ +#define _c_chash_types(SELF, KEY, VAL, MAP_ONLY, SET_ONLY) \ typedef KEY SELF##_key; \ typedef VAL SELF##_mapped; \ - typedef SZ SELF##_ssize; \ \ typedef SET_ONLY( SELF##_key ) \ MAP_ONLY( struct SELF##_value ) \ @@ -125,13 +122,13 @@ typedef union { typedef struct SELF { \ SELF##_value* table; \ uint8_t* _hashx; \ - SELF##_ssize size, bucket_count; \ + intptr_t size; \ + struct { uint64_t count: 48, maxdist: 16; } bucket; \ } SELF -#define _c_aatree_types(SELF, KEY, VAL, SZ, MAP_ONLY, SET_ONLY) \ +#define _c_aatree_types(SELF, KEY, VAL, MAP_ONLY, SET_ONLY) \ typedef KEY SELF##_key; \ typedef VAL SELF##_mapped; \ - typedef SZ SELF##_ssize; \ typedef struct SELF##_node SELF##_node; \ \ typedef SET_ONLY( SELF##_key ) \ @@ -147,12 +144,12 @@ typedef union { SELF##_value *ref; \ SELF##_node *_d; \ int _top; \ - SELF##_ssize _tn, _st[36]; \ + int32_t _tn, _st[36]; \ } SELF##_iter; \ \ typedef struct SELF { \ SELF##_node *nodes; \ - SELF##_ssize root, disp, head, size, cap; \ + int32_t root, disp, head, size, cap; \ } SELF #define _c_cstack_fixed(SELF, VAL, CAP) \ diff --git a/include/stc/priv/template.h b/include/stc/priv/template.h index 16ef51af..2605a434 100644 --- a/include/stc/priv/template.h +++ b/include/stc/priv/template.h @@ -44,10 +44,6 @@ #define i_type c_PASTE(_i_prefix, i_tag) #endif -#ifndef i_ssize - #define i_ssize intptr_t -#endif - #ifndef i_allocator #define i_allocator c #endif diff --git a/include/stc/priv/template2.h b/include/stc/priv/template2.h index 27c6a890..2e8a6c8d 100644 --- a/include/stc/priv/template2.h +++ b/include/stc/priv/template2.h @@ -30,7 +30,6 @@ #undef i_hash #undef i_rawclass #undef i_capacity -#undef i_ssize #undef i_val #undef i_val_str diff --git a/misc/benchmarks/external/ankerl/unordered_dense.h b/misc/benchmarks/external/ankerl/unordered_dense.h index faad051d..dc4de8ab 100644 --- a/misc/benchmarks/external/ankerl/unordered_dense.h +++ b/misc/benchmarks/external/ankerl/unordered_dense.h @@ -1,7 +1,7 @@ ///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// // A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. -// Version 3.1.0 +// Version 4.0.0 // https://github.com/martinus/unordered_dense // // Licensed under the MIT License <http://opensource.org/licenses/MIT>. @@ -30,12 +30,15 @@ #define ANKERL_UNORDERED_DENSE_H // see https://semver.org/spec/v2.0.0.html -#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 3 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes -#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 1 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality +#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 4 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes +#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality #define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes // API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ANKERL_UNORDERED_DENSE_VERSION_CONCAT(major, minor, patch) ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) #define ANKERL_UNORDERED_DENSE_NAMESPACE \ ANKERL_UNORDERED_DENSE_VERSION_CONCAT( \ @@ -57,9 +60,9 @@ // exceptions #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) -# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 1 +# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 1 // NOLINT(cppcoreguidelines-macro-usage) #else -# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 0 +# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 0 // NOLINT(cppcoreguidelines-macro-usage) #endif #ifdef _MSC_VER # define ANKERL_UNORDERED_DENSE_NOINLINE __declspec(noinline) @@ -89,20 +92,13 @@ # include <cstdlib> // for abort # endif -# define ANKERL_UNORDERED_DENSE_PMR 0 // NOLINT(cppcoreguidelines-macro-usage) # if defined(__has_include) # if __has_include(<memory_resource>) -# undef ANKERL_UNORDERED_DENSE_PMR -# define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage) -# define ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR \ - std::pmr::polymorphic_allocator // NOLINT(cppcoreguidelines-macro-usage) -# include <memory_resource> // for polymorphic_allocator +# define ANKERL_UNORDERED_DENSE_PMR std::pmr // NOLINT(cppcoreguidelines-macro-usage) +# include <memory_resource> // for polymorphic_allocator # elif __has_include(<experimental/memory_resource>) -# undef ANKERL_UNORDERED_DENSE_PMR -# define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage) -# define ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR \ - std::experimental::pmr::polymorphic_allocator // NOLINT(cppcoreguidelines-macro-usage) -# include <experimental/memory_resource> // for polymorphic_allocator +# define ANKERL_UNORDERED_DENSE_PMR std::experimental::pmr // NOLINT(cppcoreguidelines-macro-usage) +# include <experimental/memory_resource> // for polymorphic_allocator # endif # endif @@ -428,7 +424,7 @@ constexpr bool is_map_v = !std::is_void_v<Mapped>; // clang-format off template <typename Hash, typename KeyEqual> -constexpr bool is_transparent_v = is_detected_v<detect_is_transparent, Hash>&& is_detected_v<detect_is_transparent, KeyEqual>; +constexpr bool is_transparent_v = is_detected_v<detect_is_transparent, Hash> && is_detected_v<detect_is_transparent, KeyEqual>; // clang-format on template <typename From, typename To1, typename To2> @@ -446,19 +442,320 @@ struct base_table_type_map { // base type for set doesn't have mapped_type struct base_table_type_set {}; +} // namespace detail + +// Very much like std::deque, but faster for indexing (in most cases). As of now this doesn't implement the full std::vector +// API, but merely what's necessary to work as an underlying container for ankerl::unordered_dense::{map, set}. +// It allocates blocks of equal size and puts them into the m_blocks vector. That means it can grow simply by adding a new +// block to the back of m_blocks, and doesn't double its size like an std::vector. The disadvantage is that memory is not +// linear and thus there is one more indirection necessary for indexing. +template <typename T, typename Allocator = std::allocator<T>, size_t MaxSegmentSizeBytes = 4096> +class segmented_vector { + template <bool IsConst> + class iter_t; + +public: + using allocator_type = Allocator; + using pointer = typename std::allocator_traits<allocator_type>::pointer; + using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer; + using difference_type = typename std::allocator_traits<allocator_type>::difference_type; + using value_type = T; + using size_type = std::size_t; + using reference = T&; + using const_reference = T const&; + using iterator = iter_t<false>; + using const_iterator = iter_t<true>; + +private: + using vec_alloc = typename std::allocator_traits<Allocator>::template rebind_alloc<pointer>; + std::vector<pointer, vec_alloc> m_blocks{}; + size_t m_size{}; + + // Calculates the maximum number for x in (s << x) <= max_val + static constexpr auto num_bits_closest(size_t max_val, size_t s) -> size_t { + auto f = size_t{0}; + while (s << (f + 1) <= max_val) { + ++f; + } + return f; + } + + using self_t = segmented_vector<T, Allocator, MaxSegmentSizeBytes>; + static constexpr auto num_bits = num_bits_closest(MaxSegmentSizeBytes, sizeof(T)); + static constexpr auto num_elements_in_block = 1U << num_bits; + static constexpr auto mask = num_elements_in_block - 1U; + + /** + * Iterator class doubles as const_iterator and iterator + */ + template <bool IsConst> + class iter_t { + using ptr_t = typename std::conditional_t<IsConst, segmented_vector::const_pointer const*, segmented_vector::pointer*>; + ptr_t m_data{}; + size_t m_idx{}; + + template <bool B> + friend class iter_t; + + public: + using difference_type = segmented_vector::difference_type; + using value_type = T; + using reference = typename std::conditional_t<IsConst, value_type const&, value_type&>; + using pointer = typename std::conditional_t<IsConst, segmented_vector::const_pointer, segmented_vector::pointer>; + using iterator_category = std::forward_iterator_tag; + + iter_t() noexcept = default; + + template <bool OtherIsConst, typename = typename std::enable_if<IsConst && !OtherIsConst>::type> + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) + constexpr iter_t(iter_t<OtherIsConst> const& other) noexcept + : m_data(other.m_data) + , m_idx(other.m_idx) {} + + constexpr iter_t(ptr_t data, size_t idx) noexcept + : m_data(data) + , m_idx(idx) {} + + template <bool OtherIsConst, typename = typename std::enable_if<IsConst && !OtherIsConst>::type> + constexpr auto operator=(iter_t<OtherIsConst> const& other) noexcept -> iter_t& { + m_data = other.m_data; + m_idx = other.m_idx; + return *this; + } + + constexpr auto operator++() noexcept -> iter_t& { + ++m_idx; + return *this; + } + + constexpr auto operator+(difference_type diff) noexcept -> iter_t { + return {m_data, static_cast<size_t>(static_cast<difference_type>(m_idx) + diff)}; + } + + template <bool OtherIsConst> + constexpr auto operator-(iter_t<OtherIsConst> const& other) noexcept -> difference_type { + return static_cast<difference_type>(m_idx) - static_cast<difference_type>(other.m_idx); + } + + constexpr auto operator*() const noexcept -> reference { + return m_data[m_idx >> num_bits][m_idx & mask]; + } + + constexpr auto operator->() const noexcept -> pointer { + return &m_data[m_idx >> num_bits][m_idx & mask]; + } + + template <bool O> + constexpr auto operator==(iter_t<O> const& o) const noexcept -> bool { + return m_idx == o.m_idx; + } + + template <bool O> + constexpr auto operator!=(iter_t<O> const& o) const noexcept -> bool { + return !(*this == o); + } + }; + + // slow path: need to allocate a new segment every once in a while + void increase_capacity() { + auto ba = Allocator(m_blocks.get_allocator()); + pointer block = std::allocator_traits<Allocator>::allocate(ba, num_elements_in_block); + m_blocks.push_back(block); + } + + // Moves everything from other + void append_everything_from(segmented_vector&& other) { + reserve(size() + other.size()); + for (auto&& o : other) { + emplace_back(std::move(o)); + } + } + + // Copies everything from other + void append_everything_from(segmented_vector const& other) { + reserve(size() + other.size()); + for (auto const& o : other) { + emplace_back(o); + } + } + + void dealloc() { + auto ba = Allocator(m_blocks.get_allocator()); + for (auto ptr : m_blocks) { + std::allocator_traits<Allocator>::deallocate(ba, ptr, num_elements_in_block); + } + } + + [[nodiscard]] static constexpr auto calc_num_blocks_for_capacity(size_t capacity) { + return (capacity + num_elements_in_block - 1U) / num_elements_in_block; + } + +public: + segmented_vector() = default; + + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) + segmented_vector(Allocator alloc) + : m_blocks(vec_alloc(alloc)) {} + + segmented_vector(segmented_vector&& other, Allocator alloc) + : m_blocks(vec_alloc(alloc)) { + if (other.get_allocator() == alloc) { + *this = std::move(other); + } else { + // Oh my, allocator is different so we need to copy everything. + append_everything_from(std::move(other)); + } + } + + segmented_vector(segmented_vector&& other) noexcept + : m_blocks(std::move(other.m_blocks)) + , m_size(std::exchange(other.m_size, {})) {} + + segmented_vector(segmented_vector const& other, Allocator alloc) + : m_blocks(vec_alloc(alloc)) { + append_everything_from(other); + } + + segmented_vector(segmented_vector const& other) { + append_everything_from(other); + } + + auto operator=(segmented_vector const& other) -> segmented_vector& { + if (this == &other) { + return *this; + } + clear(); + append_everything_from(other); + return *this; + } + + auto operator=(segmented_vector&& other) noexcept -> segmented_vector& { + clear(); + dealloc(); + m_blocks = std::move(other.m_blocks); + m_size = std::exchange(other.m_size, {}); + return *this; + } + + ~segmented_vector() { + clear(); + dealloc(); + } + + [[nodiscard]] constexpr auto size() const -> size_t { + return m_size; + } + + [[nodiscard]] constexpr auto capacity() const -> size_t { + return m_blocks.size() * num_elements_in_block; + } + + // Indexing is highly performance critical + [[nodiscard]] constexpr auto operator[](size_t i) const noexcept -> T const& { + return m_blocks[i >> num_bits][i & mask]; + } + + [[nodiscard]] constexpr auto operator[](size_t i) noexcept -> T& { + return m_blocks[i >> num_bits][i & mask]; + } + + [[nodiscard]] constexpr auto begin() -> iterator { + return {m_blocks.data(), 0U}; + } + [[nodiscard]] constexpr auto begin() const -> const_iterator { + return {m_blocks.data(), 0U}; + } + [[nodiscard]] constexpr auto cbegin() const -> const_iterator { + return {m_blocks.data(), 0U}; + } + + [[nodiscard]] constexpr auto end() -> iterator { + return {m_blocks.data(), m_size}; + } + [[nodiscard]] constexpr auto end() const -> const_iterator { + return {m_blocks.data(), m_size}; + } + [[nodiscard]] constexpr auto cend() const -> const_iterator { + return {m_blocks.data(), m_size}; + } + + [[nodiscard]] constexpr auto back() -> reference { + return operator[](m_size - 1); + } + [[nodiscard]] constexpr auto back() const -> const_reference { + return operator[](m_size - 1); + } + + void pop_back() { + back().~T(); + --m_size; + } + + [[nodiscard]] auto empty() const { + return 0 == m_size; + } + + void reserve(size_t new_capacity) { + m_blocks.reserve(calc_num_blocks_for_capacity(new_capacity)); + while (new_capacity > capacity()) { + increase_capacity(); + } + } + + [[nodiscard]] auto get_allocator() const -> allocator_type { + return allocator_type{m_blocks.get_allocator()}; + } + + template <class... Args> + auto emplace_back(Args&&... args) -> reference { + if (m_size == capacity()) { + increase_capacity(); + } + auto* ptr = static_cast<void*>(&operator[](m_size)); + auto& ref = *new (ptr) T(std::forward<Args>(args)...); + ++m_size; + return ref; + } + + void clear() { + if constexpr (!std::is_trivially_destructible_v<T>) { + for (size_t i = 0, s = size(); i < s; ++i) { + operator[](i).~T(); + } + } + m_size = 0; + } + + void shrink_to_fit() { + auto ba = Allocator(m_blocks.get_allocator()); + auto num_blocks_required = calc_num_blocks_for_capacity(m_size); + while (m_blocks.size() > num_blocks_required) { + std::allocator_traits<Allocator>::deallocate(ba, m_blocks.back(), num_elements_in_block); + m_blocks.pop_back(); + } + m_blocks.shrink_to_fit(); + } +}; + +namespace detail { + // This is it, the table. Doubles as map and set, and uses `void` for T when its used as a set. template <class Key, class T, // when void, treat it as a set. class Hash, class KeyEqual, class AllocatorOrContainer, - class Bucket> + class Bucket, + bool IsSegmented> class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, base_table_type_set> { + using underlying_value_type = typename std::conditional_t<is_map_v<T>, std::pair<Key, T>, Key>; + using underlying_container_type = std::conditional_t<IsSegmented, + segmented_vector<underlying_value_type, AllocatorOrContainer>, + std::vector<underlying_value_type, AllocatorOrContainer>>; + public: - using value_container_type = std::conditional_t< - is_detected_v<detect_iterator, AllocatorOrContainer>, - AllocatorOrContainer, - typename std::vector<typename std::conditional_t<is_map_v<T>, std::pair<Key, T>, Key>, AllocatorOrContainer>>; + using value_container_type = std:: + conditional_t<is_detected_v<detect_iterator, AllocatorOrContainer>, AllocatorOrContainer, underlying_container_type>; private: using bucket_alloc = @@ -492,7 +789,8 @@ private: static_assert(std::is_trivially_copyable_v<Bucket>, "assert we can just memset / memcpy"); value_container_type m_values{}; // Contains all the key-value pairs in one densely stored container. No holes. - typename std::allocator_traits<bucket_alloc>::pointer m_buckets{}; + using bucket_pointer = typename std::allocator_traits<bucket_alloc>::pointer; + bucket_pointer m_buckets{}; size_t m_num_buckets = 0; size_t m_max_bucket_capacity = 0; float m_max_load_factor = default_max_load_factor; @@ -507,8 +805,7 @@ private: } // Helper to access bucket through pointer types - [[nodiscard]] static constexpr auto at(typename std::allocator_traits<bucket_alloc>::pointer bucket_ptr, size_t offset) - -> Bucket& { + [[nodiscard]] static constexpr auto at(bucket_pointer bucket_ptr, size_t offset) -> Bucket& { return *(bucket_ptr + static_cast<typename std::allocator_traits<bucket_alloc>::difference_type>(offset)); } @@ -578,7 +875,7 @@ private: } [[nodiscard]] static constexpr auto calc_num_buckets(uint8_t shifts) -> size_t { - return std::min(max_bucket_count(), size_t{1} << (64U - shifts)); + return (std::min)(max_bucket_count(), size_t{1} << (64U - shifts)); } [[nodiscard]] constexpr auto calc_shifts_for_size(size_t s) const -> uint8_t { @@ -983,7 +1280,7 @@ public: } [[nodiscard]] static constexpr auto max_size() noexcept -> size_t { - if constexpr (std::numeric_limits<value_idx_type>::max() == std::numeric_limits<size_t>::max()) { + if constexpr ((std::numeric_limits<value_idx_type>::max)() == (std::numeric_limits<size_t>::max)()) { return size_t{1} << (sizeof(value_idx_type) * 8 - 1); } else { return size_t{1} << (sizeof(value_idx_type) * 8); @@ -1272,7 +1569,7 @@ public: auto const last_to_end = std::distance(last, cend()); // remove elements from left to right which moves elements from the end back - auto const mid = idx_first + std::min(first_to_last, last_to_end); + auto const mid = idx_first + (std::min)(first_to_last, last_to_end); auto idx = idx_first; while (idx != mid) { erase(begin() + idx); @@ -1439,8 +1736,8 @@ public: } void rehash(size_t count) { - count = std::min(count, max_size()); - auto shifts = calc_shifts_for_size(std::max(count, size())); + count = (std::min)(count, max_size()); + auto shifts = calc_shifts_for_size((std::max)(count, size())); if (shifts != m_shifts) { m_shifts = shifts; deallocate_buckets(); @@ -1451,12 +1748,12 @@ public: } void reserve(size_t capa) { - capa = std::min(capa, max_size()); + capa = (std::min)(capa, max_size()); if constexpr (has_reserve<value_container_type>) { // std::deque doesn't have reserve(). Make sure we only call when available m_values.reserve(capa); } - auto shifts = calc_shifts_for_size(std::max(capa, size())); + auto shifts = calc_shifts_for_size((std::max)(capa, size())); if (0 == m_num_buckets || shifts < m_shifts) { m_shifts = shifts; deallocate_buckets(); @@ -1519,16 +1816,31 @@ template <class Key, class KeyEqual = std::equal_to<Key>, class AllocatorOrContainer = std::allocator<std::pair<Key, T>>, class Bucket = bucket_type::standard> -using map = detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket>; +using map = detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, false>; + +template <class Key, + class T, + class Hash = hash<Key>, + class KeyEqual = std::equal_to<Key>, + class AllocatorOrContainer = std::allocator<std::pair<Key, T>>, + class Bucket = bucket_type::standard> +using segmented_map = detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, true>; + +template <class Key, + class Hash = hash<Key>, + class KeyEqual = std::equal_to<Key>, + class AllocatorOrContainer = std::allocator<Key>, + class Bucket = bucket_type::standard> +using set = detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket, false>; template <class Key, class Hash = hash<Key>, class KeyEqual = std::equal_to<Key>, class AllocatorOrContainer = std::allocator<Key>, class Bucket = bucket_type::standard> -using set = detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket>; +using segmented_set = detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket, true>; -# if ANKERL_UNORDERED_DENSE_PMR +# if defined(ANKERL_UNORDERED_DENSE_PMR) namespace pmr { @@ -1537,10 +1849,23 @@ template <class Key, class Hash = hash<Key>, class KeyEqual = std::equal_to<Key>, class Bucket = bucket_type::standard> -using map = detail::table<Key, T, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR<std::pair<Key, T>>, Bucket>; +using map = + detail::table<Key, T, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<std::pair<Key, T>>, Bucket, false>; + +template <class Key, + class T, + class Hash = hash<Key>, + class KeyEqual = std::equal_to<Key>, + class Bucket = bucket_type::standard> +using segmented_map = + detail::table<Key, T, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<std::pair<Key, T>>, Bucket, true>; + +template <class Key, class Hash = hash<Key>, class KeyEqual = std::equal_to<Key>, class Bucket = bucket_type::standard> +using set = detail::table<Key, void, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<Key>, Bucket, false>; template <class Key, class Hash = hash<Key>, class KeyEqual = std::equal_to<Key>, class Bucket = bucket_type::standard> -using set = detail::table<Key, void, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR<Key>, Bucket>; +using segmented_set = + detail::table<Key, void, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<Key>, Bucket, true>; } // namespace pmr @@ -1558,11 +1883,18 @@ using set = detail::table<Key, void, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR_ namespace std { // NOLINT(cert-dcl58-cpp) -template <class Key, class T, class Hash, class KeyEqual, class AllocatorOrContainer, class Bucket, class Pred> +template <class Key, + class T, + class Hash, + class KeyEqual, + class AllocatorOrContainer, + class Bucket, + class Pred, + bool IsSegmented> // NOLINTNEXTLINE(cert-dcl58-cpp) -auto erase_if(ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket>& map, Pred pred) - -> size_t { - using map_t = ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket>; +auto erase_if(ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, IsSegmented>& map, + Pred pred) -> size_t { + using map_t = ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, IsSegmented>; // going back to front because erase() invalidates the end iterator auto const old_size = map.size(); diff --git a/misc/benchmarks/external/emhash/hash_table7.hpp b/misc/benchmarks/external/emhash/hash_table7.hpp index 8f8982f9..c21e145b 100644 --- a/misc/benchmarks/external/emhash/hash_table7.hpp +++ b/misc/benchmarks/external/emhash/hash_table7.hpp @@ -92,7 +92,7 @@ of resizing granularity. Ignoring variance, the expected occurrences of list siz #include "wyhash.h" #endif -#ifdef EMH_KEY +#ifdef EMH_NEW #undef EMH_KEY #undef EMH_VAL #undef EMH_PKV @@ -547,10 +547,10 @@ public: static PairT* alloc_bucket(size_type num_buckets) { -#if _WIN32 - auto* new_pairs = (PairT*)malloc(AllocSize(num_buckets)); -#else +#ifdef EMH_ALLOC auto* new_pairs = (PairT*)aligned_alloc(EMH_MALIGN, AllocSize(num_buckets)); +#else + auto* new_pairs = (PairT*)malloc(AllocSize(num_buckets)); #endif return new_pairs; } @@ -1668,16 +1668,10 @@ private: // key is not in this map. Find a place to put it. size_type find_empty_bucket(const size_type bucket_from, const size_type main_bucket) { -#ifdef EMH_ALIGN64 // only works 64bit - const auto boset = bucket_from % MASK_BIT; - auto* const align = _bitmask + bucket_from / MASK_BIT; - const auto bmask = ((size_t)align[1] << (MASK_BIT - boset)) | (align[0] >> boset); - if (EMH_LIKELY(bmask != 0)) - return bucket_from + CTZ(bmask); -#elif EMH_ITER_SAFE +#if EMH_ITER_SAFE const auto boset = bucket_from % 8; - auto* const start = (uint8_t*)_bitmask + bucket_from / 8; - size_t bmask; memcpy(&bmask, start + 0, sizeof(bmask)); bmask >>= boset;// bmask |= ((size_t)start[8] << (SIZE_BIT - boset)); + auto* const align = (uint8_t*)_bitmask + bucket_from / 8;(void)main_bucket; + size_t bmask; memcpy(&bmask, align + 0, sizeof(bmask)); bmask >>= boset;// bmask |= ((size_t)align[8] << (SIZE_BIT - boset)); if (EMH_LIKELY(bmask != 0)) return bucket_from + CTZ(bmask); #else @@ -1715,21 +1709,15 @@ private: } // key is not in this map. Find a place to put it. - size_type find_unique_empty(const size_type bucket_from, const size_t main_bucket) - { -#ifdef EMH_ALIGN64 - const auto boset = bucket_from % MASK_BIT; - auto* const align = _bitmask + bucket_from / MASK_BIT; - const auto bmask = ((size_t)align[1] << (MASK_BIT - boset)) | (align[0] >> boset); - static_assert(sizeof(size_t) > 4); -#elif EMH_ITER_SAFE + size_type find_unique_empty(const size_type bucket_from) + { const auto boset = bucket_from % 8; - auto* const start = (uint8_t*)_bitmask + bucket_from / 8; - size_t bmask; memcpy(&bmask, start + 0, sizeof(bmask)); bmask >>= boset; -#else - const auto boset = bucket_from % 8; (void)main_bucket; auto* const align = (uint8_t*)_bitmask + bucket_from / 8; - const auto bmask = (*(size_t*)(align) >> boset); //maybe not aligned and warning + +#if EMH_ITER_SAFE + size_t bmask; memcpy(&bmask, align + 0, sizeof(bmask)); bmask >>= boset; +#else + const auto bmask = (*(size_t*)(align) >> boset); //maybe not aligned and warning #endif if (EMH_LIKELY(bmask != 0)) return bucket_from + CTZ(bmask); @@ -1789,12 +1777,12 @@ private: next_bucket = find_last_bucket(next_bucket); //find a new empty and link it to tail - return EMH_BUCKET(_pairs, next_bucket) = find_unique_empty(next_bucket, bucket); + return EMH_BUCKET(_pairs, next_bucket) = find_unique_empty(next_bucket); } #if EMH_INT_HASH static constexpr uint64_t KC = UINT64_C(11400714819323198485); - inline uint64_t hash64(uint64_t key) + inline static uint64_t hash64(uint64_t key) { #if __SIZEOF_INT128__ && EMH_INT_HASH == 1 __uint128_t r = key; r *= KC; diff --git a/misc/benchmarks/shootout_hashmaps.cpp b/misc/benchmarks/shootout_hashmaps.cpp index debff31d..05ec33e7 100644 --- a/misc/benchmarks/shootout_hashmaps.cpp +++ b/misc/benchmarks/shootout_hashmaps.cpp @@ -35,7 +35,7 @@ KHASH_MAP_INIT_INT64(ii, IValue) // cmap template expansion #define i_key IKey #define i_val IValue -//#define i_ssize int32_t // enable 2^K buckets like the rest. +#define i_expandby 2 // enable 2^K buckets like the rest. #define i_tag ii #define i_max_load_factor MAX_LOAD_FACTOR / 100.0f #include <stc/cmap.h> diff --git a/misc/benchmarks/various/sso_bench.cpp b/misc/benchmarks/various/sso_bench.cpp index 993ff1bb..9841c296 100644 --- a/misc/benchmarks/various/sso_bench.cpp +++ b/misc/benchmarks/various/sso_bench.cpp @@ -10,58 +10,61 @@ #include <stc/cstack.h> #define i_type StcSet +#define i_expandby 2 #define i_val_str -#include <stc/csset.h> +#include <stc/cset.h> #include <vector> using StdVec = std::vector<std::string>; -#include <set> -using StdSet = std::set<std::string>; +//#include "../external/ankerl/robin_hood.h" +//using StdSet = robin_hood::unordered_flat_set<std::string>; -static const int BENCHMARK_SIZE = 2000000; -static const int MAX_STRING_SIZE = 50; +#include <unordered_set> +using StdSet = std::unordered_set<std::string>; + + +static const int BENCHMARK_SIZE = 250000; +static const int MAX_STRING_SIZE = 100; static const char CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=+-"; using time_point = std::chrono::high_resolution_clock::time_point; -static inline std::string randomString_STD(int strsize) { - std::string s(strsize, 0); - char* p = &s[0]; +static inline const char* randomString(int strsize) { + static char str[256]; union { uint64_t u8; uint8_t b[8]; } r; for (int i = 0; i < strsize; ++i) { if ((i & 7) == 0) r.u8 = crand() & 0x3f3f3f3f3f3f3f3f; - p[i] = CHARS[r.b[i & 7]]; + str[i] = CHARS[r.b[i & 7]]; } - return s; + str[strsize] = 0; + return str; } -static inline cstr randomString_STC(int strsize) { - cstr s = cstr_with_size(strsize, 0); - char* p = cstr_data(&s); - union { uint64_t u8; uint8_t b[8]; } r; - for (int i = 0; i < strsize; ++i) { - if ((i & 7) == 0) r.u8 = crand() & 0x3f3f3f3f3f3f3f3f; - p[i] = CHARS[r.b[i & 7]]; - } - return s; + + +static inline void addRandomString(StdVec& vec, int strsize) { + vec.push_back(randomString(strsize)); } +static inline void addRandomString(StcVec& vec, int strsize) { + StcVec_emplace(&vec, randomString(strsize)); +} -void addRandomString(StdVec& vec, int strsize) { - vec.push_back(std::move(randomString_STD(strsize))); +static inline void addRandomString(StdSet& set, int strsize) { + set.insert(randomString(strsize)); } -void addRandomString(StcVec& vec, int strsize) { - StcVec_push(&vec, randomString_STC(strsize)); +static inline void addRandomString(StcSet& set, int strsize) { + StcSet_emplace(&set, randomString(strsize)); } -void addRandomString(StdSet& set, int strsize) { - set.insert(std::move(randomString_STD(strsize))); +static inline bool getRandomString(const StdSet& set, int strsize) { + return set.find(randomString(strsize)) != set.end(); } -void addRandomString(StcSet& set, int strsize) { - StcSet_insert(&set, randomString_STC(strsize)); +static inline bool getRandomString(const StcSet& set, int strsize) { + return StcSet_contains(&set, randomString(strsize)); } @@ -78,6 +81,22 @@ int benchmark(C& container, const int n, const int strsize) { return (int)duration; } +template <class C> +int benchmark_lookup(C& container, const int n, const int strsize) { + for (int i = 0; i < n; i++) + addRandomString(container, strsize); + + time_point t1 = std::chrono::high_resolution_clock::now(); + int found = 0; + for (int i = 0; i < n; i++) + found += (int)getRandomString(container, strsize); + + time_point t2 = std::chrono::high_resolution_clock::now(); + const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count(); + std::cerr << (strsize ? strsize : 32) << "\t" << duration << '\t' << found; + return (int)duration; +} + int main() { uint64_t seed = 4321; @@ -88,48 +107,75 @@ int main() { csrand(seed); sum = 0, n = 0; std::cerr << "\nstrsize\tmsecs\tstd::vector<std::string>, size=" << BENCHMARK_SIZE << "\n"; - for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 4) { StdVec vec; vec.reserve(BENCHMARK_SIZE); sum += benchmark(vec, BENCHMARK_SIZE, strsize), ++n; std::cout << '\t' << vec.front() << '\n'; } - std::cout << "Avg:\t" << sum/n << '\n'; + std::cout << "Avg:\t" << sum/n << "ms\n"; csrand(seed); sum = 0, n = 0; std::cerr << "\nstrsize\tmsecs\tcvec<cstr>, size=" << BENCHMARK_SIZE << "\n"; - for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 4) { StcVec vec = StcVec_with_capacity(BENCHMARK_SIZE); sum += benchmark(vec, BENCHMARK_SIZE, strsize), ++n; std::cout << '\t' << cstr_str(&vec.data[0]) << '\n'; StcVec_drop(&vec); } - std::cout << "Avg:\t" << sum/n << '\n'; + std::cout << "Avg:\t" << sum/n << "ms\n"; + + // INSERT: SORTED SET WITH STRINGS + + csrand(seed); + sum = 0, n = 0; + std::cerr << "\nstrsize\tmsecs\tinsert: robin_hood::unordered_flat_set<std::string>, size=" << BENCHMARK_SIZE/2 << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 4) { + StdSet set; set.reserve(BENCHMARK_SIZE/2); + sum += benchmark(set, BENCHMARK_SIZE/2, strsize), ++n; + std::cout << '\t' << *set.begin() << '\n'; + } + std::cout << "Avg:\t" << sum/n << "ms\n"; - // SORTED SET WITH STRINGS csrand(seed); sum = 0, n = 0; - std::cerr << "\nstrsize\tmsecs\tstd::set<std::string>, size=" << BENCHMARK_SIZE/16 << "\n"; - for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { - StdSet set; - sum += benchmark(set, BENCHMARK_SIZE/16, strsize), ++n; + std::cerr << "\nstrsize\tmsecs\tinsert: cset<cstr>, size=" << BENCHMARK_SIZE/2 << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 4) { + StcSet set = StcSet_with_capacity(BENCHMARK_SIZE/2); + sum += benchmark(set, BENCHMARK_SIZE/2, strsize), ++n; + std::cout << '\t' << cstr_str(StcSet_begin(&set).ref) << '\n'; + StcSet_drop(&set); + } + std::cout << "Avg:\t" << sum/n << "ms\n"; + + // LOOKUP: SORTED SET WITH STRINGS + + csrand(seed); + sum = 0, n = 0; + std::cerr << "\nstrsize\tmsecs\tfind: robin_hood::unordered_flat_set<std::string>, size=" << BENCHMARK_SIZE/2 << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 4) { + StdSet set; set.reserve(BENCHMARK_SIZE/2); + sum += benchmark_lookup(set, BENCHMARK_SIZE/2, strsize), ++n; std::cout << '\t' << *set.begin() << '\n'; } - std::cout << "Avg:\t" << sum/n << '\n'; + std::cout << "Avg:\t" << sum/n << "ms\n"; csrand(seed); sum = 0, n = 0; - std::cerr << "\nstrsize\tmsecs\tcsset<cstr>, size=" << BENCHMARK_SIZE/16 << "\n"; - for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { - StcSet set = StcSet_with_capacity(BENCHMARK_SIZE/16); - sum += benchmark(set, BENCHMARK_SIZE/16, strsize), ++n; - std::cout << '\t' << cstr_str(StcSet_front(&set)) << '\n'; + std::cerr << "\nstrsize\tmsecs\tfind: cset<cstr>, size=" << BENCHMARK_SIZE/2 << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 4) { + StcSet set = StcSet_with_capacity(BENCHMARK_SIZE/2); + sum += benchmark_lookup(set, BENCHMARK_SIZE/2, strsize), ++n; + std::cout << '\t' << cstr_str(StcSet_begin(&set).ref) << '\n'; StcSet_drop(&set); } - std::cout << "Avg:\t" << sum/n << '\n'; + std::cout << "Avg:\t" << sum/n << "ms\n"; + std::cerr << "sizeof(std::string) : " << sizeof(std::string) << std::endl - << "sizeof(cstr) : " << sizeof(cstr) << std::endl; + << "sizeof(cstr) : " << sizeof(cstr) << std::endl + << "sizeof(StdSet) : " << sizeof(StdSet) << std::endl + << "sizeof(StcSet) : " << sizeof(StcSet) << std::endl; return 0; } |
