Compare commits

...

2 Commits

4 changed files with 81 additions and 31 deletions

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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;