summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTyge Løvset <[email protected]>2023-04-18 17:48:47 +0200
committerTyge Løvset <[email protected]>2023-04-18 17:48:47 +0200
commita913490c248f6cfdca3481d81d6d344f4d066cb9 (patch)
treeb805826d496a8d5c8a449731b6c8bc3b1d056979
parent462adebf55c77697fbb379a62941c00b876c3f6a (diff)
downloadSTC-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.md2
-rw-r--r--docs/cset_api.md2
-rw-r--r--docs/csmap_api.md1
-rw-r--r--docs/csset_api.md1
-rw-r--r--include/stc/cbits.h70
-rw-r--r--include/stc/ccommon.h6
-rw-r--r--include/stc/cmap.h77
-rw-r--r--include/stc/csmap.h79
-rw-r--r--include/stc/forward.h23
-rw-r--r--include/stc/priv/template.h4
-rw-r--r--include/stc/priv/template2.h1
-rw-r--r--misc/benchmarks/external/ankerl/unordered_dense.h414
-rw-r--r--misc/benchmarks/external/emhash/hash_table7.hpp44
-rw-r--r--misc/benchmarks/shootout_hashmaps.cpp2
-rw-r--r--misc/benchmarks/various/sso_bench.cpp134
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;
}