From 796ee021457fe1da6e91ec706ad6cc50e52b21f5 Mon Sep 17 00:00:00 2001 From: Ludovic 'Archivist' Lagouardette Date: Mon, 30 Nov 2020 15:23:04 +0100 Subject: [PATCH] Support for deletion added --- include/lfhmap.hpp | 90 ++++++++++++++++++++++++++++++++++++++++++++++ tests.sh | 10 ++++-- tests/test07.cpp | 51 ++++++++++++++++++++++++++ tests/test08.cpp | 51 ++++++++++++++++++++++++++ 4 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 tests/test07.cpp create mode 100644 tests/test08.cpp diff --git a/include/lfhmap.hpp b/include/lfhmap.hpp index 45a6b27..eee14af 100644 --- a/include/lfhmap.hpp +++ b/include/lfhmap.hpp @@ -7,7 +7,10 @@ #include #include #include +#include +#include +using namespace std::chrono_literals; /** Pensé en un mundo sin memoria, sin tiempo; consideré la posibilidad de un lenguaje que ignorara los sustantivos, un lenguaje de verbos impersonales y de indeclinables @@ -72,12 +75,88 @@ namespace mct20 { template class bucket { + constexpr static uint32_t delete_mode = 65536; + std::atomic delete_lock; + + void reader_lock() { + while(delete_lock.fetch_add(1) >= delete_mode) { + delete_lock.fetch_sub(1); + std::this_thread::yield(); + } + return; + } + + void writer_lock() { + while(delete_lock.fetch_add(delete_mode) >= delete_mode) { + delete_lock.fetch_sub(delete_mode); + std::this_thread::yield(); + } + while(delete_lock.load() != delete_mode) { + } + return; + } + + void reader_unlock() { + delete_lock.fetch_sub(1); + } + + void writer_unlock() { + delete_lock.fetch_sub(delete_mode); + } + + struct RGuard { + bucket& master; + RGuard(bucket& m) + : master(m) + {master.reader_lock();} + + ~RGuard() + {master.reader_unlock();} + }; + + struct WGuard { + bucket& master; + WGuard(bucket& m) + : master(m) + {master.writer_lock();} + + ~WGuard() + {master.writer_unlock();} + }; public: bucket() : start{nullptr} {} + void remove(size_t hash, const K& key) { + WGuard _g(*this); + auto it = start.load(); + auto prev = &start; + do{ + if(it == nullptr) return; + while(it->contents.hash != hash) + { + prev = reinterpret_cast(&(it->contents.next)); + it = (node*)it->contents.next.load(); + if(it == nullptr) return; + } + if(it->contents.key == key) { + prev->store(reinterpret_cast(it->contents.next.load())); + it->contents.references.fetch_sub(1); + while(it->contents.references.load()!=0) { + std::this_thread::yield(); + } + delete it->contents.ptr; + delete it; + return; + } + prev = reinterpret_cast(&(it->contents.next)); + it = (node*)it->contents.next.load(); + } while(true); + } + void push(size_t hash, const K& key, const V& value) { + RGuard _g(*this); auto t = new node{ .contents = node_contents{ .key{key}, @@ -101,6 +180,7 @@ namespace mct20 { } std::optional> get(const size_t hash, const K& key) { + RGuard _g(*this); auto v = start.load(); while(v) { if(v->contents.references.fetch_add(1)!=0) @@ -186,6 +266,16 @@ public: return; } + void remove(const K& key) { + const auto l = hash{}(key); + auto& ref = buckets[l%bucket_count]; + ref.remove(l, key); + const auto l2 = _details_::rotl(l, sizeof(size_t)*4); + auto& ref2 = buckets[l2%bucket_count]; + ref2.remove(l2, key); + return; + } + lfhmap() { for(auto& a : buckets) { a.start = nullptr; diff --git a/tests.sh b/tests.sh index c5dc507..45e8d43 100755 --- a/tests.sh +++ b/tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -g++ -pthread -Iinclude -std=c++17 -O3 -g tests/test01.cpp +g++ -Iinclude -std=c++17 -s -O3 tests/test01.cpp ./a.out || echo "FAILURE ON TEST01" g++ -pthread -Iinclude -std=c++17 -O3 -g tests/test02.cpp @@ -16,4 +16,10 @@ g++ -pthread -Iinclude -std=c++17 -O3 -g tests/test05.cpp ./a.out || echo "FAILURE ON TEST05" g++ -pthread -Iinclude -std=c++17 -O3 -g tests/test06.cpp -./a.out || echo "FAILURE ON TEST06" \ No newline at end of file +./a.out || echo "FAILURE ON TEST06" + +g++ -pthread -Iinclude -std=c++17 -O3 -g tests/test07.cpp +./a.out || echo "FAILURE ON TEST07" + +g++ -pthread -Iinclude -std=c++17 -O3 -g tests/test08.cpp +./a.out || echo "FAILURE ON TEST08" \ No newline at end of file diff --git a/tests/test07.cpp b/tests/test07.cpp new file mode 100644 index 0000000..f0f6bc7 --- /dev/null +++ b/tests/test07.cpp @@ -0,0 +1,51 @@ +#include "lfhmap.hpp" +#include +#include +#include +#include + +template +void repeat(size_t nb, fn v) { + while(nb--) { + v(); + } +} + +int main() { + constexpr size_t thread_cnt = 16; + size_t v = 0; + auto map = new mct20::lfhmap(); + std::vector> finals; + auto start = std::chrono::high_resolution_clock::now(); + repeat(thread_cnt, [&](){ + size_t v2 = v; + v++; + finals.push_back(std::async(std::launch::async, [&map, v2](){ + for(int a = v2; a < 250000; a+=thread_cnt) { + map->set(a, std::to_string(a)); + } + for(int a = v2; a < 250000; a+=thread_cnt) { + if(auto acc = map->get(a); acc) { + const std::string& t = acc.value(); + if(t != std::to_string(a)) + return 1; + } else + return 1; + } + for(int a = v2; a < 250000; a+=thread_cnt) { + map->remove(a); + } + return 0; + })); + }); + + for(auto& a : finals) a.wait(); + int ret = 0; + for(auto& a : finals) ret += a.get(); + + auto time = std::chrono::high_resolution_clock::now() - start; + std::cout << "Test 07 took " << std::chrono::duration_cast(time).count() << "ms" << std::endl; + std::cout << "Per 1R1W1D ("<< thread_cnt << " threads) " << std::chrono::duration_cast(time).count()/250000 << "ns" << std::endl; + + return ret; +} \ No newline at end of file diff --git a/tests/test08.cpp b/tests/test08.cpp new file mode 100644 index 0000000..08965a7 --- /dev/null +++ b/tests/test08.cpp @@ -0,0 +1,51 @@ +#include "lfhmap.hpp" +#include +#include +#include +#include + +template +void repeat(size_t nb, fn v) { + while(nb--) { + v(); + } +} + +int main() { + constexpr size_t thread_cnt = 16; + size_t v = 0; + auto map = new mct20::lfhmap(); + std::vector> finals; + auto start = std::chrono::high_resolution_clock::now(); + repeat(thread_cnt, [&](){ + size_t v2 = v; + v++; + finals.push_back(std::async(std::launch::async, [&map, v2](){ + for(int a = v2; a < 250000; a+=thread_cnt) { + map->set(a, std::to_string(a)); + } + for(int a = v2; a < 250000; a+=thread_cnt) { + if(auto acc = map->get(a); acc) { + const std::string& t = acc.value(); + if(t != std::to_string(a)) + return 1; + } else + return 1; + } + for(int a = v2; a < 250000; a+=thread_cnt) { + map->remove(a); + } + return 0; + })); + }); + + for(auto& a : finals) a.wait(); + int ret = 0; + for(auto& a : finals) ret += a.get(); + + auto time = std::chrono::high_resolution_clock::now() - start; + std::cout << "Test 08 took " << std::chrono::duration_cast(time).count() << "ms" << std::endl; + std::cout << "Per 1R1W1D ("<< thread_cnt << " threads) " << std::chrono::duration_cast(time).count()/250000 << "ns" << std::endl; + + return ret; +} \ No newline at end of file