feat(hash): add memory recycling hash table

This commit is contained in:
fallenoak 2023-03-26 15:49:41 -05:00 committed by GitHub
parent 06dbb7d1c8
commit 8a232d34f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 158 additions and 5 deletions

View file

@ -4,5 +4,6 @@
#include "storm/hash/Hashkey.hpp" #include "storm/hash/Hashkey.hpp"
#include "storm/hash/TSHashObject.hpp" #include "storm/hash/TSHashObject.hpp"
#include "storm/hash/TSHashTable.hpp" #include "storm/hash/TSHashTable.hpp"
#include "storm/hash/TSHashTableReuse.hpp"
#endif #endif

View file

@ -0,0 +1,15 @@
#ifndef STORM_HASH_TS_OBJECT_CHUNK_HPP
#define STORM_HASH_TS_OBJECT_CHUNK_HPP
#include "storm/Array.hpp"
#include "storm/List.hpp"
template <class T, class TKey>
class TSHashObjectChunk {
public:
// Member variables
TSGrowableArray<T> m_array;
TSLink<TSHashObjectChunk<T, TKey>> m_link;
};
#endif

View file

@ -14,20 +14,22 @@ class TSHashTable {
TSGrowableArray<STORM_EXPLICIT_LIST(T, m_linktoslot)> m_slotlistarray; TSGrowableArray<STORM_EXPLICIT_LIST(T, m_linktoslot)> m_slotlistarray;
uint32_t m_slotmask = -1; uint32_t m_slotmask = -1;
// Virtual member functions
virtual void InternalDelete(T* ptr);
virtual T* InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags);
virtual ~TSHashTable();
virtual void Destroy();
// Member functions // Member functions
~TSHashTable();
void Clear(); void Clear();
uint32_t ComputeSlot(uint32_t hashval); uint32_t ComputeSlot(uint32_t hashval);
void Destroy();
int32_t GetLinkOffset(); int32_t GetLinkOffset();
T* Head(); T* Head();
void Initialize(); void Initialize();
bool Initialized(); bool Initialized();
void Insert(T* ptr, uint32_t hashval, const TKey& key); void Insert(T* ptr, uint32_t hashval, const TKey& key);
void InternalClear(int32_t warn); void InternalClear(int32_t warn);
void InternalDelete(T* ptr);
void InternalLinkNode(T* ptr, uint32_t hashval); void InternalLinkNode(T* ptr, uint32_t hashval);
T* InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags);
T* InternalNewNode(uint32_t, size_t extrabytes, uint32_t flags); T* InternalNewNode(uint32_t, size_t extrabytes, uint32_t flags);
int32_t MonitorFullness(uint32_t slot); int32_t MonitorFullness(uint32_t slot);
T* New(const char* str, size_t extrabytes, uint32_t flags); T* New(const char* str, size_t extrabytes, uint32_t flags);
@ -49,6 +51,14 @@ void TSHashTable<T, TKey>::Clear() {
this->InternalClear(0); this->InternalClear(0);
} }
template <class T, class TKey>
void TSHashTable<T, TKey>::Destroy() {
this->InternalClear(1);
this->m_fullnessIndicator = 0;
this->m_slotmask = -1;
this->m_slotlistarray.Clear();
}
template <class T, class TKey> template <class T, class TKey>
uint32_t TSHashTable<T, TKey>::ComputeSlot(uint32_t hashval) { uint32_t TSHashTable<T, TKey>::ComputeSlot(uint32_t hashval) {
return hashval & this->m_slotmask; return hashval & this->m_slotmask;

View file

@ -0,0 +1,86 @@
#ifndef STORM_HASH_TS_HASH_TABLE_REUSE_HPP
#define STORM_HASH_TS_HASH_TABLE_REUSE_HPP
#include "storm/hash/TSHashObjectChunk.hpp"
#include "storm/hash/TSHashTable.hpp"
#include "storm/Error.hpp"
#include "storm/List.hpp"
#include <cstddef>
#include <cstdint>
template <class T, class TKey>
class TSHashTableReuse : public TSHashTable<T, TKey> {
public:
// Virtual member functions
virtual ~TSHashTableReuse();
virtual void Destroy();
private:
using object_chunk_t = TSHashObjectChunk<T, TKey>;
// Member variables
STORM_EXPLICIT_LIST(T, m_linktoslot) m_reuseList;
uint32_t m_chunkSize = 16;
STORM_EXPLICIT_LIST(object_chunk_t, m_link) m_chunkList;
// Virtual member functions
virtual void InternalDelete(T* ptr);
virtual T* InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags);
// Member functions
void Destructor();
};
template <class T, class TKey>
TSHashTableReuse<T, TKey>::~TSHashTableReuse() {
this->Destructor();
}
template <class T, class TKey>
void TSHashTableReuse<T, TKey>::Destroy() {
this->Clear();
this->Destructor();
}
template <class T, class TKey>
void TSHashTableReuse<T, TKey>::Destructor() {
this->m_chunkList.Clear();
this->m_reuseList.Clear();
this->m_chunkSize = 16;
}
template <class T, class TKey>
void TSHashTableReuse<T, TKey>::InternalDelete(T* ptr) {
this->m_fulllist.UnlinkNode(ptr);
this->m_reuseList.LinkNode(ptr, 1, nullptr);
}
template <class T, class TKey>
T* TSHashTableReuse<T, TKey>::InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags) {
STORM_ASSERT(!extrabytes);
auto node = this->m_reuseList.Head();
if (!node) {
object_chunk_t* chunk;
while (true) {
chunk = this->m_chunkList.Head();
if (chunk && chunk->m_array.Reserved()) {
break;
}
chunk = this->m_chunkList.NewNode(1, 0, 0x0);
chunk->m_array.Reserve(this->m_chunkSize, 0);
}
node = chunk->m_array.New();
}
listptr->LinkNode(node, 1, nullptr);
return node;
}
#endif

View file

@ -2,7 +2,7 @@
#include "test/Test.hpp" #include "test/Test.hpp"
struct TestHashObject : TSHashObject<TestHashObject, HASHKEY_STRI> { struct TestHashObject : TSHashObject<TestHashObject, HASHKEY_STRI> {
uint32_t index = 0; uint32_t index = 255;
}; };
TEST_CASE("TSHashTable", "[hash]") { TEST_CASE("TSHashTable", "[hash]") {
@ -34,3 +34,44 @@ TEST_CASE("TSHashTable::Clear", "[hash]") {
REQUIRE(hashTable.Head() == nullptr); REQUIRE(hashTable.Head() == nullptr);
} }
} }
TEST_CASE("TSHashTableReuse", "[hash]") {
SECTION("constructs correctly") {
TSHashTableReuse<TestHashObject, HASHKEY_STRI> hashTable;
REQUIRE(hashTable.Head() == nullptr);
}
}
TEST_CASE("TSHashTableReuse::New", "[hash]") {
SECTION("allocates new object correctly") {
TSHashTableReuse<TestHashObject, HASHKEY_STRI> hashTable;
auto object = hashTable.New("testKey1", 0, 0x0);
REQUIRE(object != nullptr);
REQUIRE(object->index == 255);
}
SECTION("recycles memory correctly") {
TSHashTableReuse<TestHashObject, HASHKEY_STRI> hashTable;
auto object1 = hashTable.New("testKey1", 0, 0x0);
auto object1Ptr = reinterpret_cast<uintptr_t>(object1);
hashTable.Clear();
auto object2 = hashTable.New("testKey2", 0, 0x0);
auto object2Ptr = reinterpret_cast<uintptr_t>(object2);
hashTable.Clear();
auto object3 = hashTable.New("testKey3", 0, 0x0);
auto object3Ptr = reinterpret_cast<uintptr_t>(object3);
REQUIRE(object1Ptr == object2Ptr);
REQUIRE(object1Ptr == object3Ptr);
REQUIRE(hashTable.Ptr("testKey1") == nullptr);
REQUIRE(hashTable.Ptr("testKey2") == nullptr);
REQUIRE(hashTable.Ptr("testKey3") != nullptr);
REQUIRE(hashTable.Ptr("testKey3")->index == 255);
}
}