Browse Source

Support for deletion added

master
Ludovic 'Archivist' Lagouardette 3 years ago
parent
commit
796ee02145
4 changed files with 200 additions and 2 deletions
  1. +90
    -0
      include/lfhmap.hpp
  2. +8
    -2
      tests.sh
  3. +51
    -0
      tests/test07.cpp
  4. +51
    -0
      tests/test08.cpp

+ 90
- 0
include/lfhmap.hpp View File

@ -7,7 +7,10 @@
#include <memory>
#include <cassert>
#include <optional>
#include <chrono>
#include <thread>
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<typename K, typename V>
class bucket {
constexpr static uint32_t delete_mode = 65536;
std::atomic<uint32_t> 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<node_ptr*>(&(it->contents.next));
it = (node*)it->contents.next.load();
if(it == nullptr) return;
}
if(it->contents.key == key) {
prev->store(reinterpret_cast<node*>(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<node_ptr*>(&(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<accessor<V>> 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;

+ 8
- 2
tests.sh View File

@ -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"
./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"

+ 51
- 0
tests/test07.cpp View File

@ -0,0 +1,51 @@
#include "lfhmap.hpp"
#include <string>
#include <iostream>
#include <future>
#include <vector>
template<typename fn>
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<size_t, std::string, 8000>();
std::vector<std::future<int>> 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<std::chrono::milliseconds>(time).count() << "ms" << std::endl;
std::cout << "Per 1R1W1D ("<< thread_cnt << " threads) " << std::chrono::duration_cast<std::chrono::nanoseconds>(time).count()/250000 << "ns" << std::endl;
return ret;
}

+ 51
- 0
tests/test08.cpp View File

@ -0,0 +1,51 @@
#include "lfhmap.hpp"
#include <string>
#include <iostream>
#include <future>
#include <vector>
template<typename fn>
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<size_t, std::string, 40000>();
std::vector<std::future<int>> 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<std::chrono::milliseconds>(time).count() << "ms" << std::endl;
std::cout << "Per 1R1W1D ("<< thread_cnt << " threads) " << std::chrono::duration_cast<std::chrono::nanoseconds>(time).count()/250000 << "ns" << std::endl;
return ret;
}

Loading…
Cancel
Save