Skip to content

Instantly share code, notes, and snippets.

@commander-trashdin
Last active September 7, 2021 18:18
Show Gist options
  • Select an option

  • Save commander-trashdin/d67d69c79ab8944d8f3442e49678b0a0 to your computer and use it in GitHub Desktop.

Select an option

Save commander-trashdin/d67d69c79ab8944d8f3442e49678b0a0 to your computer and use it in GitHub Desktop.
concurrent hash map
#include <unordered_map>
#include <mutex>
#include <functional>
#include <forward_list>
#include <vector>
#include <utility>
#include <algorithm>
#include <memory>
template <class K, class V, class Hash = std::hash<K>>
class ConcurrentHashMap {
public:
ConcurrentHashMap(const Hash& hasher = Hash()) : ConcurrentHashMap(kUndefinedSize, hasher) {
}
explicit ConcurrentHashMap(int expected_size, const Hash& hasher = Hash())
: ConcurrentHashMap(expected_size, kDefaultConcurrencyLevel, hasher) {
}
ConcurrentHashMap(int expected_size, int expected_threads_count, const Hash& hasher = Hash())
: hasher_(hasher), mutexes_(expected_threads_count) {
table_.resize(expected_size);
}
bool Insert(const K& key, const V& value) {
if (static_cast<float>(count) / table_.size() > 0.75) {
Resize(std::max(static_cast<size_t>(2), 2 * count + 1));
}
auto hash = hasher_(key) % table_.size();
auto group = hash / (table_.size() / mutexes_.size());
std::lock_guard<std::mutex> lock(*mutexes_.at(group));
bool key_esists =
(std::find_if(table_[hash].begin(), table_[hash].end(), [&key](const auto& pair) {
return pair.first == key;
}) != std::end(table_[hash]));
if (key_esists) {
return false;
}
table_[hash].emplace_front(key, value);
count++;
return true;
// probably bs
}
bool Erase(const K& key) {
auto hash = hasher_(key) % table_.size();
auto group = hash / (table_.size() / mutexes_.size());
std::lock_guard<std::mutex> lock(*mutexes_.at(group));
bool key_esists =
(std::find_if(table_[hash].begin(), table_[hash].end(), [&key](const auto& pair) {
return pair.first == key;
}) != std::end(table_[hash]));
if (!key_esists) {
return false;
}
table_[hash].remove_if([&key](const auto& pair) { return pair.first == key; });
count--;
if (static_cast<float>(count) / table_.size() < 0.25) {
Resize(count / 2);
}
return true;
}
void Clear() {
std::lock_guard<std::mutex> lock(resize_mutex_);
std::vector<std::unique_lock<std::mutex>> locks;
locks.reserve(mutexes_.size());
for (auto&& mut : mutexes_) {
locks.emplace_back(*mut);
}
table_.clear();
}
size_t Size() const {
std::lock_guard<std::mutex> lock(resize_mutex_);
std::vector<std::unique_lock<std::mutex>> locks;
locks.reserve(mutexes_.size());
for (auto&& mut : mutexes_) {
locks.emplace_back(*mut);
}
return table_.size();
}
std::pair<bool, V> Find(const K& key) const {
auto hash = hasher_(key) % table_.size();
auto group = hash / (table_.size() / mutexes_.size());
std::lock_guard<std::mutex> lock(*mutexes_.at(group));
auto it = std::find_if(table_[hash].begin(), table_[hash].end(),
[&key](const auto& pair) { return pair.first == key; });
return it == std::end(table_[hash]) ? std::make_pair(false, V())
: std::make_pair(true, it->second);
}
const V At(const K& key) const {
auto hash = hasher_(key) % table_.size();
auto group = hash / (table_.size() / mutexes_.size());
std::lock_guard<std::mutex> lock(*mutexes_.at(group));
auto it = std::find_if(table_[hash].begin(), table_[hash].end(),
[&key](const auto& pair) { return pair.first == key; });
return it->second;
}
static const int kDefaultConcurrencyLevel;
static const int kUndefinedSize;
private:
void Resize(size_t newsize) {
std::lock_guard<std::mutex> lock(resize_mutex_);
std::vector<std::unique_lock<std::mutex>> locks;
locks.reserve(mutexes_.size());
for (auto&& mut : mutexes_) {
locks.emplace_back(*mut);
}
std::vector<std::forward_list<std::pair<K, V>>> newtable;
newtable.reserve(newsize);
for (const auto& bucket : table_) {
for (const auto& pair : bucket) {
auto hash = hasher_(pair.first) % newsize;
newtable[hash].emplace_front(pair.first, pair.second);
}
}
table_ = newtable;
}; // globally locks
size_t count = 0;
Hash hasher_;
std::vector<std::forward_list<std::pair<K, V>>> table_;
mutable std::vector<std::unique_ptr<std::mutex>> mutexes_;
mutable std::mutex resize_mutex_;
};
template <class K, class V, class Hash>
const int ConcurrentHashMap<K, V, Hash>::kDefaultConcurrencyLevel = 8;
template <class K, class V, class Hash>
const int ConcurrentHashMap<K, V, Hash>::kUndefinedSize = -1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment