mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Add tommy_hashdyn_reserve to pre-allocate hash buckets for expected elements
This commit is contained in:
parent
0d2f466b11
commit
93c39d5526
2 changed files with 58 additions and 24 deletions
|
|
@ -28,36 +28,47 @@ TOMMY_API void tommy_hashdyn_done(tommy_hashdyn* hashdyn)
|
|||
*/
|
||||
static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_uint_t new_bucket_bit)
|
||||
{
|
||||
tommy_size_t bucket_bit;
|
||||
tommy_size_t bucket_max;
|
||||
tommy_size_t new_bucket_max;
|
||||
tommy_size_t new_bucket_mask;
|
||||
tommy_size_t bucket_bit = hashdyn->bucket_bit;
|
||||
tommy_size_t bucket_max = hashdyn->bucket_max;
|
||||
tommy_size_t new_bucket_max = (tommy_size_t)1 << new_bucket_bit;
|
||||
tommy_size_t new_bucket_mask = new_bucket_max - 1;
|
||||
tommy_hashdyn_node** new_bucket;
|
||||
|
||||
bucket_bit = hashdyn->bucket_bit;
|
||||
bucket_max = hashdyn->bucket_max;
|
||||
|
||||
new_bucket_max = (tommy_size_t)1 << new_bucket_bit;
|
||||
new_bucket_mask = new_bucket_max - 1;
|
||||
|
||||
/* allocate the new vector using malloc() and not calloc() */
|
||||
/* because data is fully initialized in the update process */
|
||||
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
|
||||
|
||||
/* reinsert all the elements */
|
||||
if (new_bucket_bit > bucket_bit) {
|
||||
if (!hashdyn->count) {
|
||||
/* empty table: allocate zeroed memory with calloc without looping */
|
||||
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(new_bucket_max, sizeof(tommy_hashdyn_node*)));
|
||||
} else if (new_bucket_bit == bucket_bit + 1) {
|
||||
tommy_size_t i;
|
||||
|
||||
/* grow */
|
||||
/* grow by 1 bit: zero the two buckets inline in loop to preserve cache locality */
|
||||
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
|
||||
|
||||
for (i = 0; i < bucket_max; ++i) {
|
||||
tommy_hashdyn_node* j;
|
||||
|
||||
/* setup the new two buckets */
|
||||
new_bucket[i] = 0;
|
||||
new_bucket[i + bucket_max] = 0;
|
||||
|
||||
/* reinsert the bucket */
|
||||
j = hashdyn->bucket[i];
|
||||
while (j) {
|
||||
tommy_hashdyn_node* j_next = j->next;
|
||||
tommy_size_t pos = j->index & new_bucket_mask;
|
||||
if (new_bucket[pos])
|
||||
tommy_list_insert_tail_not_empty(new_bucket[pos], j);
|
||||
else
|
||||
tommy_list_insert_first(&new_bucket[pos], j);
|
||||
j = j_next;
|
||||
}
|
||||
}
|
||||
} else if (new_bucket_bit > bucket_bit) {
|
||||
tommy_size_t i;
|
||||
|
||||
/* grow by multiple bits with elements: allocate zeroed memory and reinsert */
|
||||
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(new_bucket_max, sizeof(tommy_hashdyn_node*)));
|
||||
|
||||
for (i = 0; i < bucket_max; ++i) {
|
||||
tommy_hashdyn_node* j = hashdyn->bucket[i];
|
||||
|
||||
while (j) {
|
||||
tommy_hashdyn_node* j_next = j->next;
|
||||
tommy_size_t pos = j->index & new_bucket_mask;
|
||||
|
|
@ -71,12 +82,11 @@ static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_uint_t new_bucket
|
|||
} else {
|
||||
tommy_size_t i;
|
||||
|
||||
/* shrink */
|
||||
for (i = 0; i < new_bucket_max; ++i) {
|
||||
/* setup the new bucket with the lower bucket*/
|
||||
new_bucket[i] = hashdyn->bucket[i];
|
||||
/* shrink with elements: all buckets are overwritten, no pre-zeroing needed */
|
||||
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
|
||||
|
||||
/* concat the upper bucket */
|
||||
for (i = 0; i < new_bucket_max; ++i) {
|
||||
new_bucket[i] = hashdyn->bucket[i];
|
||||
tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]);
|
||||
}
|
||||
}
|
||||
|
|
@ -110,6 +120,19 @@ tommy_inline void hashdyn_shrink_step(tommy_hashdyn* hashdyn)
|
|||
tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1);
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_hashdyn_reserve(tommy_hashdyn* hashdyn, tommy_size_t count)
|
||||
{
|
||||
tommy_uint_t bucket_bit = tommy_ilog2(count | 1) + 2;
|
||||
|
||||
if (bucket_bit < TOMMY_HASHDYN_BIT)
|
||||
bucket_bit = TOMMY_HASHDYN_BIT;
|
||||
|
||||
if (bucket_bit <= hashdyn->bucket_bit)
|
||||
return;
|
||||
|
||||
tommy_hashdyn_resize(hashdyn, bucket_bit);
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash)
|
||||
{
|
||||
tommy_size_t pos = hash & hashdyn->bucket_mask;
|
||||
|
|
@ -256,3 +279,4 @@ TOMMY_API void tommy_hashdyn_to_list(tommy_hashdyn* hashdyn, tommy_list* list)
|
|||
memset(hashdyn->bucket, 0, hashdyn->bucket_max * sizeof(tommy_hashdyn_node*));
|
||||
hashdyn->count = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -155,6 +155,15 @@ TOMMY_API void tommy_hashdyn_init(tommy_hashdyn* hashdyn);
|
|||
*/
|
||||
TOMMY_API void tommy_hashdyn_done(tommy_hashdyn* hashdyn);
|
||||
|
||||
/**
|
||||
* Pre-allocates buckets for the specified number of elements.
|
||||
*
|
||||
* It ensures that the hashtable has enough buckets allocated to contain
|
||||
* the specified number of elements without triggering a dynamic resize.
|
||||
* \param count Number of elements to reserve space for.
|
||||
*/
|
||||
TOMMY_API void tommy_hashdyn_reserve(tommy_hashdyn* hashdyn, tommy_size_t count);
|
||||
|
||||
/**
|
||||
* Inserts an element in the hashtable.
|
||||
*/
|
||||
|
|
@ -308,3 +317,4 @@ TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn);
|
|||
TOMMY_API void tommy_hashdyn_to_list(tommy_hashdyn* hashdyn, tommy_list* list);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue