Skip to content

Commit b2f94ee

Browse files
committed
Optimize HashMap
Removed unnecessary calls to the _hash method. Replaced fastmod with &. Removed a lot of unnecessary checks. Optimized _look_up by moving checks if there are no collisions. Added _FORCE_INLINE_ for is_empty, _lookup, _insert_with_hash_and_element methods. Added fast 0.75 multiplier. Used memset. Delete allocations for elements without iterating on hashes. Added a check if there are many collisions (DEV). Fixed a lot of collisions in strings. Move hash functions to cpp file. Add hash_mixed method to StringName and NodePath.
1 parent 30bb49e commit b2f94ee

11 files changed

Lines changed: 675 additions & 329 deletions

File tree

core/string/node_path.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,30 @@
3030

3131
#include "node_path.h"
3232

33+
#include "core/templates/hashfuncs.h"
3334
#include "core/variant/variant.h"
3435

3536
void NodePath::_update_hash_cache() const {
3637
uint32_t h = data->absolute ? 1 : 0;
3738
int pc = data->path.size();
3839
const StringName *sn = data->path.ptr();
40+
uint32_t hash_mixed = h;
3941
for (int i = 0; i < pc; i++) {
40-
h = h ^ sn[i].hash();
42+
uint32_t string_hash = sn[i].hash();
43+
h = h ^ string_hash;
44+
hash_mixed = hash_djb2_one_32(string_hash, hash_mixed); // Don't change to murmur3 because StringName uses djb2!
4145
}
4246
int spc = data->subpath.size();
4347
const StringName *ssn = data->subpath.ptr();
4448
for (int i = 0; i < spc; i++) {
45-
h = h ^ ssn[i].hash();
49+
uint32_t string_hash = ssn[i].hash();
50+
h = h ^ string_hash;
51+
hash_mixed = hash_djb2_one_32(string_hash, hash_mixed);
4652
}
4753

4854
data->hash_cache_valid = true;
4955
data->hash_cache = h;
56+
data->hash_mixed_cache = hash_fmix32(hash_mixed);
5057
}
5158

5259
void NodePath::prepend_period() {

core/string/node_path.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class NodePath {
4343
bool absolute;
4444
mutable bool hash_cache_valid;
4545
mutable uint32_t hash_cache;
46+
mutable uint32_t hash_mixed_cache;
4647
};
4748

4849
mutable Data *data = nullptr;
@@ -78,6 +79,16 @@ class NodePath {
7879
return data->hash_cache;
7980
}
8081

82+
_FORCE_INLINE_ uint32_t hash_mixed() const {
83+
if (!data) {
84+
return 0;
85+
}
86+
if (!data->hash_cache_valid) {
87+
_update_hash_cache();
88+
}
89+
return data->hash_mixed_cache;
90+
}
91+
8192
operator String() const;
8293
bool is_empty() const;
8394

core/string/string_name.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,14 +270,16 @@ StringName::StringName(const char *p_name, bool p_static) {
270270
}
271271

272272
const uint32_t hash = String::hash(p_name);
273-
const uint32_t idx = hash & STRING_TABLE_MASK;
273+
const uint32_t hash_mixed = hash_fmix32(hash);
274+
275+
const uint32_t idx = hash_mixed & STRING_TABLE_MASK;
274276

275277
MutexLock lock(mutex);
276278
_data = _table[idx];
277279

278280
while (_data) {
279281
// compare hash first
280-
if (_data->hash == hash && _data->operator==(p_name)) {
282+
if (_data->hash_mixed == hash_mixed && _data->operator==(p_name)) {
281283
break;
282284
}
283285
_data = _data->next;
@@ -305,6 +307,7 @@ StringName::StringName(const char *p_name, bool p_static) {
305307
_data->cname = nullptr;
306308
_data->next = _table[idx];
307309
_data->prev = nullptr;
310+
_data->hash_mixed = hash_mixed;
308311

309312
#ifdef DEBUG_ENABLED
310313
if (unlikely(debug_stringname)) {
@@ -327,14 +330,16 @@ StringName::StringName(const StaticCString &p_static_string, bool p_static) {
327330
ERR_FAIL_COND(!p_static_string.ptr || !p_static_string.ptr[0]);
328331

329332
const uint32_t hash = String::hash(p_static_string.ptr);
330-
const uint32_t idx = hash & STRING_TABLE_MASK;
333+
const uint32_t hash_mixed = hash_fmix32(hash);
334+
335+
const uint32_t idx = hash_mixed & STRING_TABLE_MASK;
331336

332337
MutexLock lock(mutex);
333338
_data = _table[idx];
334339

335340
while (_data) {
336341
// compare hash first
337-
if (_data->hash == hash && _data->operator==(p_static_string.ptr)) {
342+
if (_data->hash_mixed == hash_mixed && _data->operator==(p_static_string.ptr)) {
338343
break;
339344
}
340345
_data = _data->next;
@@ -362,6 +367,8 @@ StringName::StringName(const StaticCString &p_static_string, bool p_static) {
362367
_data->cname = p_static_string.ptr;
363368
_data->next = _table[idx];
364369
_data->prev = nullptr;
370+
371+
_data->hash_mixed = hash_mixed;
365372
#ifdef DEBUG_ENABLED
366373
if (unlikely(debug_stringname)) {
367374
// Keep in memory, force static.
@@ -385,13 +392,14 @@ StringName::StringName(const String &p_name, bool p_static) {
385392
}
386393

387394
const uint32_t hash = p_name.hash();
388-
const uint32_t idx = hash & STRING_TABLE_MASK;
395+
const uint32_t hash_mixed = hash_fmix32(hash);
396+
const uint32_t idx = hash_mixed & STRING_TABLE_MASK;
389397

390398
MutexLock lock(mutex);
391399
_data = _table[idx];
392400

393401
while (_data) {
394-
if (_data->hash == hash && _data->operator==(p_name)) {
402+
if (_data->hash_mixed == hash_mixed && _data->operator==(p_name)) {
395403
break;
396404
}
397405
_data = _data->next;
@@ -419,6 +427,7 @@ StringName::StringName(const String &p_name, bool p_static) {
419427
_data->cname = nullptr;
420428
_data->next = _table[idx];
421429
_data->prev = nullptr;
430+
_data->hash_mixed = hash_mixed;
422431
#ifdef DEBUG_ENABLED
423432
if (unlikely(debug_stringname)) {
424433
// Keep in memory, force static.
@@ -441,10 +450,11 @@ StringName StringName::search(const char *p_name) {
441450
return StringName();
442451
}
443452

444-
const uint32_t hash = String::hash(p_name);
453+
const uint32_t hash = hash_fmix32(String::hash(p_name));
445454
const uint32_t idx = hash & STRING_TABLE_MASK;
446455

447456
MutexLock lock(mutex);
457+
448458
_Data *_data = _table[idx];
449459

450460
while (_data) {
@@ -476,10 +486,11 @@ StringName StringName::search(const char32_t *p_name) {
476486
return StringName();
477487
}
478488

479-
const uint32_t hash = String::hash(p_name);
489+
const uint32_t hash = hash_fmix32(String::hash(p_name));
480490
const uint32_t idx = hash & STRING_TABLE_MASK;
481491

482492
MutexLock lock(mutex);
493+
483494
_Data *_data = _table[idx];
484495

485496
while (_data) {
@@ -500,10 +511,11 @@ StringName StringName::search(const char32_t *p_name) {
500511
StringName StringName::search(const String &p_name) {
501512
ERR_FAIL_COND_V(p_name.is_empty(), StringName());
502513

503-
const uint32_t hash = p_name.hash();
514+
const uint32_t hash = hash_fmix32(p_name.hash());
504515
const uint32_t idx = hash & STRING_TABLE_MASK;
505516

506517
MutexLock lock(mutex);
518+
507519
_Data *_data = _table[idx];
508520

509521
while (_data) {

core/string/string_name.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class StringName {
6565
bool operator!=(const char *p_name) const;
6666

6767
int idx = 0;
68+
uint32_t hash_mixed = 0;
6869
uint32_t hash = 0;
6970
_Data *prev = nullptr;
7071
_Data *next = nullptr;
@@ -145,6 +146,16 @@ class StringName {
145146
return get_empty_hash();
146147
}
147148
}
149+
150+
// Used by HashMapHasherDefault. Don`t use it as seed to create other hash.
151+
_FORCE_INLINE_ uint32_t hash_mixed() const {
152+
if (unlikely(_data == nullptr)) {
153+
return get_empty_hash();
154+
}
155+
156+
return _data->hash_mixed;
157+
}
158+
148159
_FORCE_INLINE_ const void *data_unique_pointer() const {
149160
return (void *)_data;
150161
}

core/templates/hash_map.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,12 @@ bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right) {
4141
}
4242
return res;
4343
}
44+
45+
// Explicit instantiation.
46+
template class HashMap<int, int>;
47+
template class HashMap<String, int>;
48+
template class HashMap<StringName, bool>;
49+
template class HashMap<StringName, StringName>;
50+
template class HashMap<StringName, String>;
51+
template class HashMap<StringName, Variant>;
52+
template class HashMap<StringName, int>;

0 commit comments

Comments
 (0)