Compare commits
2 Commits
cecea2eaff
...
2a2c435b88
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a2c435b88 | |||
| 30cbc7cd1c |
28
headers/Containers/ContainerOptions.hpp
Normal file
28
headers/Containers/ContainerOptions.hpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: Dora "cat" <cat@thenight.club>
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
#ifndef GUARD_TOURMALINE_CONTAINEROPTIONS_H
|
||||||
|
#define GUARD_TOURMALINE_CONTAINEROPTIONS_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Tourmaline::Containers {
|
||||||
|
struct HashmapOptions {
|
||||||
|
float loadFactor = 0.75f;
|
||||||
|
float minimizeFactor = 0.20f;
|
||||||
|
float leaningFactor = 2.5f;
|
||||||
|
std::size_t minimumBucketCount = 256;
|
||||||
|
std::size_t reservedBucketSpace = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DualKeyMapOptions {
|
||||||
|
std::uint64_t baseReservation = 2048;
|
||||||
|
};
|
||||||
|
} // namespace Tourmaline::Containers
|
||||||
|
#endif
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#define GUARD_TOURMALINE_DUALKEYMAP_H
|
#define GUARD_TOURMALINE_DUALKEYMAP_H
|
||||||
#include "../Concepts.hpp"
|
#include "../Concepts.hpp"
|
||||||
#include "../Systems/Logging.hpp"
|
#include "../Systems/Logging.hpp"
|
||||||
|
#include "ContainerOptions.hpp"
|
||||||
#include "Hashmap.hpp"
|
#include "Hashmap.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
|
|
||||||
namespace Tourmaline::Containers {
|
namespace Tourmaline::Containers {
|
||||||
template <Concepts::Hashable AKey, Concepts::Hashable BKey, typename Value,
|
template <Concepts::Hashable AKey, Concepts::Hashable BKey, typename Value,
|
||||||
uint64_t baseReservation = 2048>
|
DualKeyMapOptions Options = {}>
|
||||||
class DualkeyMap {
|
class DualkeyMap {
|
||||||
public:
|
public:
|
||||||
// Return Types
|
// Return Types
|
||||||
@@ -49,7 +50,7 @@ public:
|
|||||||
using Entry = std::tuple<const AKey &, const BKey &, Value &>;
|
using Entry = std::tuple<const AKey &, const BKey &, Value &>;
|
||||||
|
|
||||||
// Construct/Destruct
|
// Construct/Destruct
|
||||||
DualkeyMap() { hashList.reserve(baseReservation); }
|
DualkeyMap() { hashList.reserve(Options.baseReservation); }
|
||||||
~DualkeyMap() {
|
~DualkeyMap() {
|
||||||
// I'm sure there is a better way to do this
|
// I'm sure there is a better way to do this
|
||||||
for (DualkeyHash *hash : hashList) {
|
for (DualkeyHash *hash : hashList) {
|
||||||
@@ -300,12 +301,9 @@ private:
|
|||||||
Key *keyToCompare;
|
Key *keyToCompare;
|
||||||
OppositeKey *oppositeKey;
|
OppositeKey *oppositeKey;
|
||||||
|
|
||||||
// TO DO, merge std::vector with Containers::Hashmap!!
|
Containers::Hashmap<OppositeKey, MultiQueryResult<OppositeKey, keyCount>,
|
||||||
std::vector<MultiQueryResult<OppositeKey, keyCount>> queryResults;
|
{8.0f, 0.01f, 2.5f, 2048, 8}> // Aggressive hashmap :o
|
||||||
queryResults.reserve(2048);
|
queryResults;
|
||||||
Containers::Hashmap<OppositeKey, MultiQueryResult<OppositeKey, keyCount> *,
|
|
||||||
8.0f, 2048, 0.01f> // Aggressive hashmap :o
|
|
||||||
locations;
|
|
||||||
|
|
||||||
for (DualkeyHash *hash : hashList) {
|
for (DualkeyHash *hash : hashList) {
|
||||||
// Tombstone
|
// Tombstone
|
||||||
@@ -328,20 +326,22 @@ private:
|
|||||||
// The code above was done to make this code more uniform
|
// The code above was done to make this code more uniform
|
||||||
for (uint64_t index = 0; index < keyCount; index++) {
|
for (uint64_t index = 0; index < keyCount; index++) {
|
||||||
if (keyHashes[index] == hashToCompare && keys[index] == *keyToCompare) {
|
if (keyHashes[index] == hashToCompare && keys[index] == *keyToCompare) {
|
||||||
if (locations.Has(*oppositeKey)) {
|
if (queryResults.Has(*oppositeKey)) [[likely]] {
|
||||||
locations.Get(*oppositeKey)->valueQueryResults[index] =
|
auto &entry = queryResults.Get(*oppositeKey);
|
||||||
&hash->value;
|
entry.valueQueryResults[index] = &hash->value;
|
||||||
} else {
|
++entry.howManyFound;
|
||||||
queryResults.emplace_back(oppositeKey);
|
break;
|
||||||
auto &newRecord = queryResults.back();
|
|
||||||
newRecord.valueQueryResults[index] = &hash->value;
|
|
||||||
locations.Insert(*oppositeKey, &newRecord);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queryResults
|
||||||
|
.Insert(*oppositeKey,
|
||||||
|
MultiQueryResult<OppositeKey, keyCount>(oppositeKey))
|
||||||
|
.valueQueryResults[index] = &hash->value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryResults;
|
return queryResults.ExtractValuesToArray();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace Tourmaline::Containers
|
} // namespace Tourmaline::Containers
|
||||||
|
|||||||
@@ -10,20 +10,21 @@
|
|||||||
#define GUARD_TOURMALINE_HASHMAP_H
|
#define GUARD_TOURMALINE_HASHMAP_H
|
||||||
#include "../Concepts.hpp"
|
#include "../Concepts.hpp"
|
||||||
#include "../Systems/Logging.hpp"
|
#include "../Systems/Logging.hpp"
|
||||||
|
#include "ContainerOptions.hpp"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Tourmaline::Containers {
|
namespace Tourmaline::Containers {
|
||||||
template <Concepts::Hashable Key, typename Value, float loadFactor = 0.75f,
|
template <Concepts::Hashable Key, typename Value, HashmapOptions Options = {}>
|
||||||
std::size_t minimumBucketCount = 256, float minimizeFactor = 0.20f>
|
|
||||||
class Hashmap {
|
class Hashmap {
|
||||||
public:
|
public:
|
||||||
Hashmap() { storage.resize(minimumBucketCount); }
|
Hashmap() { storage.resize(Options.minimumBucketCount); }
|
||||||
~Hashmap() { Clear(); }
|
~Hashmap() { Clear(); }
|
||||||
|
|
||||||
Value &Insert(Key key, Value value) {
|
Value &Insert(Key key, Value value) {
|
||||||
if (currentLoadFactor >= loadFactor && currentlyRehashing == false) {
|
if (currentLoadFactor >= Options.loadFactor &&
|
||||||
|
currentlyRehashing == false) {
|
||||||
rehash();
|
rehash();
|
||||||
}
|
}
|
||||||
std::size_t keyHash = std::hash<Key>{}(key),
|
std::size_t keyHash = std::hash<Key>{}(key),
|
||||||
@@ -35,6 +36,8 @@ public:
|
|||||||
Systems::Logging::Log("Trying to insert the same key twice! Throwing...",
|
Systems::Logging::Log("Trying to insert the same key twice! Throwing...",
|
||||||
"Hashmap", Systems::Logging::LogLevel::Error,
|
"Hashmap", Systems::Logging::LogLevel::Error,
|
||||||
Has(key));
|
Has(key));
|
||||||
|
} else {
|
||||||
|
storage[keyHashPosition].reserve(Options.reservedBucketSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
storage[keyHashPosition].emplace_back(key, std::move(value), keyHash);
|
storage[keyHashPosition].emplace_back(key, std::move(value), keyHash);
|
||||||
@@ -56,7 +59,7 @@ public:
|
|||||||
});
|
});
|
||||||
|
|
||||||
currentLoadFactor = (--count) / static_cast<float>(bucketCount);
|
currentLoadFactor = (--count) / static_cast<float>(bucketCount);
|
||||||
if (currentLoadFactor <= minimizeFactor) {
|
if (currentLoadFactor <= Options.minimizeFactor) {
|
||||||
rehash();
|
rehash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,6 +103,25 @@ public:
|
|||||||
"Hashmap", Systems::Logging::LogLevel::Error);
|
"Hashmap", Systems::Logging::LogLevel::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard("Discarding an expensive operation!")]]
|
||||||
|
std::vector<Value> ExtractValuesToArray() {
|
||||||
|
std::vector<Value> result;
|
||||||
|
result.reserve(count);
|
||||||
|
|
||||||
|
for (bucket &entry : storage) {
|
||||||
|
for (hashStorage &hash : entry) {
|
||||||
|
result.emplace_back(std::move(hash.value));
|
||||||
|
}
|
||||||
|
entry.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
bucketCount = Options.minimumBucketCount;
|
||||||
|
std::vector<bucket> newStorage;
|
||||||
|
storage.swap(newStorage);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void Clear() noexcept {
|
void Clear() noexcept {
|
||||||
storage.clear();
|
storage.clear();
|
||||||
count = 0;
|
count = 0;
|
||||||
@@ -115,16 +137,15 @@ private:
|
|||||||
// Minimum
|
// Minimum
|
||||||
goalSize = goalSize == 0 ? count : goalSize;
|
goalSize = goalSize == 0 ? count : goalSize;
|
||||||
float wouldBeLoadFactor = goalSize / static_cast<float>(bucketCount);
|
float wouldBeLoadFactor = goalSize / static_cast<float>(bucketCount);
|
||||||
if (wouldBeLoadFactor < loadFactor && wouldBeLoadFactor > minimizeFactor)
|
if (wouldBeLoadFactor < Options.loadFactor &&
|
||||||
[[unlikely]] {
|
wouldBeLoadFactor > Options.minimizeFactor) [[unlikely]] {
|
||||||
return false; // No rehashing is required
|
return false; // No rehashing is required
|
||||||
}
|
}
|
||||||
|
|
||||||
// Putting it closer to minimizeFactor
|
// Putting it closer to minimizeFactor
|
||||||
std::size_t goalBucketCount =
|
std::size_t goalBucketCount = goalSize / preferredLoadFactor;
|
||||||
goalSize / ((loadFactor + minimizeFactor) / 2.5); // Magic number
|
if (goalBucketCount < Options.minimumBucketCount) [[unlikely]] {
|
||||||
if (goalBucketCount < minimumBucketCount) [[unlikely]] {
|
goalBucketCount = Options.minimumBucketCount;
|
||||||
goalBucketCount = minimumBucketCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to reallocate
|
// No need to reallocate
|
||||||
@@ -162,8 +183,10 @@ private:
|
|||||||
|
|
||||||
using bucket = std::vector<hashStorage>;
|
using bucket = std::vector<hashStorage>;
|
||||||
std::vector<bucket> storage;
|
std::vector<bucket> storage;
|
||||||
std::size_t count = 0, bucketCount = minimumBucketCount;
|
std::size_t count = 0, bucketCount = Options.minimumBucketCount;
|
||||||
float currentLoadFactor = 0;
|
float currentLoadFactor = 0,
|
||||||
|
preferredLoadFactor = (Options.loadFactor + Options.minimizeFactor) /
|
||||||
|
Options.leaningFactor;
|
||||||
bool currentlyRehashing = false; // Lock for Insert in rehash
|
bool currentlyRehashing = false; // Lock for Insert in rehash
|
||||||
};
|
};
|
||||||
} // namespace Tourmaline::Containers
|
} // namespace Tourmaline::Containers
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include <Systems/ECS.hpp>
|
#include <Systems/ECS.hpp>
|
||||||
#include <Systems/ECS/BuiltinComponents.hpp>
|
#include <Systems/ECS/BuiltinComponents.hpp>
|
||||||
#include <Systems/Random.hpp>
|
#include <Systems/Random.hpp>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
using namespace Tourmaline::Systems;
|
using namespace Tourmaline::Systems;
|
||||||
using namespace ECS;
|
using namespace ECS;
|
||||||
|
|||||||
Reference in New Issue
Block a user