Inherits concurrent_hash_map_internal::hash_map_base< Key, T, MutexType, ScopedLockType >.
|
| concurrent_hash_map () |
| Construct empty table.
|
|
| concurrent_hash_map (size_type n) |
| Construct empty table with n preallocated buckets. More...
|
|
| concurrent_hash_map (const concurrent_hash_map &table) |
| Copy constructor.
|
|
| concurrent_hash_map (concurrent_hash_map &&table) |
| Move constructor.
|
|
template<typename I > |
| concurrent_hash_map (I first, I last) |
| Construction table with copying iteration range.
|
|
| concurrent_hash_map (std::initializer_list< value_type > il) |
| Construct table with initializer list.
|
|
void | runtime_initialize () |
| Initialize persistent concurrent hash map after process restart. More...
|
|
void | runtime_initialize (bool graceful_shutdown) |
|
concurrent_hash_map & | operator= (const concurrent_hash_map &table) |
| Assignment Not thread safe. More...
|
|
concurrent_hash_map & | operator= (std::initializer_list< value_type > il) |
| Assignment Not thread safe. More...
|
|
void | rehash (size_type n=0) |
| Rehashes and optionally resizes the whole table. More...
|
|
void | clear () |
| Clear hash map content Not thread safe. More...
|
|
void | free_data () |
| Destroys the concurrent_hash_map. More...
|
|
| ~concurrent_hash_map () |
| free_data should be called before concurrent_hash_map destructor is called. More...
|
|
iterator | begin () |
|
iterator | end () |
|
const_iterator | begin () const |
|
const_iterator | end () const |
|
size_type | size () const |
|
bool | empty () const |
|
size_type | max_size () const |
| Upper bound on size.
|
|
size_type | bucket_count () const |
|
void | swap (concurrent_hash_map &table) |
| Swap two instances. More...
|
|
size_type | count (const Key &key) const |
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
size_type | count (const K &key) const |
| This overload only participates in overload resolution if the qualified-id Hash::transparent_key_equal is valid and denotes a type. More...
|
|
bool | find (const_accessor &result, const Key &key) const |
| Find item and acquire a read lock on the item. More...
|
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
bool | find (const_accessor &result, const K &key) const |
| Find item and acquire a read lock on the item. More...
|
|
bool | find (accessor &result, const Key &key) |
| Find item and acquire a write lock on the item. More...
|
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
bool | find (accessor &result, const K &key) |
| Find item and acquire a write lock on the item. More...
|
|
bool | insert (const_accessor &result, const Key &key) |
| Insert item (if not already present) and acquire a read lock on the item. More...
|
|
bool | insert (accessor &result, const Key &key) |
| Insert item (if not already present) and acquire a write lock on the item. More...
|
|
bool | insert (const_accessor &result, const value_type &value) |
| Insert item by copying if there is no such key present already and acquire a read lock on the item. More...
|
|
bool | insert (accessor &result, const value_type &value) |
| Insert item by copying if there is no such key present already and acquire a write lock on the item. More...
|
|
bool | insert (const value_type &value) |
| Insert item by copying if there is no such key present already. More...
|
|
bool | insert (const_accessor &result, value_type &&value) |
| Insert item by copying if there is no such key present already and acquire a read lock on the item. More...
|
|
bool | insert (accessor &result, value_type &&value) |
| Insert item by copying if there is no such key present already and acquire a write lock on the item. More...
|
|
bool | insert (value_type &&value) |
| Insert item by copying if there is no such key present already. More...
|
|
template<typename I > |
void | insert (I first, I last) |
| Insert range [first, last) More...
|
|
void | insert (std::initializer_list< value_type > il) |
| Insert initializer list. More...
|
|
template<typename M > |
bool | insert_or_assign (const key_type &key, M &&obj) |
| Inserts item if there is no such key present already, assigns provided value otherwise. More...
|
|
template<typename M > |
bool | insert_or_assign (key_type &&key, M &&obj) |
| Inserts item if there is no such key present already, assigns provided value otherwise. More...
|
|
template<typename K , typename M , typename = typename std::enable_if< concurrent_hash_map_internal::has_transparent_key_equal< hasher>::value && std::is_constructible<key_type, K>::value, K>::type> |
bool | insert_or_assign (K &&key, M &&obj) |
| Inserts item if there is no such key-comparable type present already, assigns provided value otherwise. More...
|
|
bool | erase (const Key &key) |
| Remove element with corresponding key. More...
|
|
pobj_defrag_result | defragment (double start_percent=0, double amount_percent=100) |
| Defragment the given (by 'start_percent' and 'amount_percent') part of buckets of the hash map. More...
|
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
bool | erase (const K &key) |
| Remove element with corresponding key. More...
|
|
template<typename Key, typename T, typename Hash, typename KeyEqual, typename MutexType, typename ScopedLockType>
class pmem::obj::concurrent_hash_map< Key, T, Hash, KeyEqual, MutexType, ScopedLockType >
Persistent memory aware implementation of Intel TBB concurrent_hash_map
The implementation is based on a concurrent hash table algorithm (https://arxiv.org/ftp/arxiv/papers/1509/1509.02235.pdf) where elements are assigned to buckets based on a hash code calculated from a key. In addition to concurrent find, insert, and erase operations, the algorithm employs resizing and on-demand per-bucket rehashing. The hash table consists of an array of buckets, and each bucket consists of a list of nodes and a read-write lock to control concurrent access by multiple threads.
There's no STL counterpart for this container (only the TBB's implementation).
Each time, the pool with concurrent_hash_map is being opened, the concurrent_hash_map requires runtime_initialize() to be called (in order to recalculate mask and restore the size).
find(), insert(), erase() (and all overloads) are guaranteed to be thread-safe.
When a thread holds accessor to an element with a certain key, it is not allowed to call find, insert nor erase with that key.
MutexType defines type of read write lock used in concurrent_hash_map. ScopedLockType defines a mutex wrapper that provides RAII-style mechanism for owning a mutex. It should implement following methods and constructors: ScopedLockType() ScopedLockType(rw_mutex_type &m, bool write = true) void acquire(rw_mutex_type &m, bool write) void release() bool try_acquire(rw_mutex_type &m, bool write)
and optionally: bool upgrade_to_writer() bool downgrade_to_reader() bool is_writer (variable)
Implementing all optional methods and supplying is_writer variable can improve performance if MutexType supports efficient upgrading and downgrading operations.
- Note
- In some cases, when testing with Valgrind, helgrind and drd might report lock ordering errors. This might happen when calling find, insert or erase while already holding an accessor to some element.
The typical usage example would be:
#include <iostream>
#include <vector>
using hashmap_type = concurrent_hash_map<p<int>, p<int>>;
const int THREADS_NUM = 30;
struct root {
persistent_ptr<hashmap_type> pptr;
};
int
main(int argc, char *argv[])
{
pool<root> pop;
bool remove_hashmap = false;
if (argc < 2)
std::cerr << "usage: " << argv[0]
<< " file-name [remove_hashmap]" << std::endl;
auto path = argv[1];
if (argc == 3)
try {
std::cerr << e.what() << std::endl;
return -1;
}
try {
auto &r = pop.root()->pptr;
if (r == nullptr) {
r = make_persistent<hashmap_type>();
});
r->runtime_initialize();
} else {
r->runtime_initialize();
try {
r->defragment();
std::cerr << "Defragmentation exception: "
<< e.what() << std::endl;
throw;
}
}
auto &map = *r;
std::cout << map.size() << std::endl;
std::vector<std::thread> threads;
threads.reserve(static_cast<size_t>(THREADS_NUM));
for (int i = 0; i < THREADS_NUM / 3; ++i) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
map.insert(
hashmap_type::value_type(i, i));
}
});
}
for (int i = 0; i < THREADS_NUM / 3; ++i) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
map.erase(i);
}
});
}
for (int i = 0; i < THREADS_NUM / 3; ++i) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
hashmap_type::accessor acc;
bool res = map.find(acc, i);
if (res) {
assert(acc->first == i);
assert(acc->second >= i);
acc->second.get_rw() += 1;
pop.persist(acc->second);
}
}
});
}
for (auto &t : threads) {
t.join();
}
try {
map.defragment();
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
} catch (const std::range_error &e) {
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
} catch (const std::runtime_error &e) {
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
}
if (remove_hashmap) {
try {
map.clear();
std::cerr << "Clear exception: " << e.what()
<< std::endl;
throw;
std::cerr << "Clear exception: " << e.what()
<< std::endl;
throw;
}
map.free_data();
delete_persistent<hashmap_type>(r);
r = nullptr;
});
}
std::cerr << "Exception occurred: " << e.what() << std::endl;
} catch (std::exception &e) {
std::cerr << "Exception occurred: " << e.what() << std::endl;
}
try {
pop.close();
} catch (const std::logic_error &e) {
std::cerr << "Exception: " << e.what() << std::endl;
return -1;
}
return 0;
}
Custom defrag error class.
Definition: pexceptions.hpp:212
Custom lock error class.
Definition: pexceptions.hpp:121
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:676
static pool< T > open(const std::string &path, const std::string &layout)
Opens an existing object store memory pool.
Definition: pool.hpp:672
Custom pool error class.
Definition: pexceptions.hpp:84
Custom transaction error class.
Definition: pexceptions.hpp:156
Custom out of memory error class.
Definition: pexceptions.hpp:144
A persistent version of concurrent hash map implementation Ref: https://arxiv.org/abs/1509....
basic_string< char > string
The most typical string usage - the char specialization.
Definition: string.hpp:24
Main libpmemobj namespace.
Definition: allocation_flag.hpp:18
Resides on pmem property template.
Persistent smart pointer.
The example of storing strings without necessity of using transactions would be:
#include <iostream>
#include <string>
#include <vector>
using hashmap_type =
const int THREADS_NUM = 30;
struct root {
};
int
main(int argc, char *argv[])
{
bool remove_hashmap = false;
int retval = 0;
try {
if (argc < 2) {
std::cerr
<< "usage: " << argv[0]
<< " file-name [remove_hashmap]" << std::endl
<< "Before running this example, run:"
<< std::endl
<< "pmempool create obj --layout=\"cmap_string\" --size 1G path_to_a_pool"
<< std::endl;
return -1;
}
auto path = argv[1];
if (argc == 3)
try {
std::cerr << e.what() << std::endl;
return -1;
}
auto &r = pop.
root()->pptr;
if (r == nullptr) {
r = pmem::obj::make_persistent<hashmap_type>();
});
r->runtime_initialize();
} else {
r->runtime_initialize();
try {
r->defragment();
std::cerr << "Defragmentation exception: "
<< e.what() << std::endl;
throw;
}
}
auto &map = *r;
std::cout << " Number of elements at application startup: "
<< map.size() << std::endl;
std::vector<std::thread> threads;
threads.reserve(static_cast<size_t>(THREADS_NUM));
for (int j = 0; j < THREADS_NUM / 3; ++j) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
map.insert_or_assign(i,
std::to_string(i));
}
});
}
for (int j = 0; j < THREADS_NUM / 3; ++j) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
hashmap_type::const_accessor acc;
bool res = map.find(acc, i);
if (res) {
assert(acc->first == i);
*element = &acc->second;
std::cout << element->
c_str()
<< std::endl;
}
}
});
}
for (int j = 0; j < THREADS_NUM / 3; ++j) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
map.erase(i);
}
});
}
for (auto &t : threads) {
t.join();
}
try {
map.defragment();
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
} catch (const std::range_error &e) {
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
} catch (const std::runtime_error &e) {
std::cerr << "Defragmentation exception: " << e.what()
<< std::endl;
throw;
}
if (remove_hashmap) {
try {
map.clear();
std::cerr << "Clear exception: " << e.what()
<< std::endl;
throw;
std::cerr << "Clear exception: " << e.what()
<< std::endl;
throw;
}
map.free_data();
pmem::obj::delete_persistent<hashmap_type>(r);
r = nullptr;
});
}
std::cerr << "Exception occurred: " << e.what() << std::endl;
retval = -127;
} catch (const std::exception &e) {
std::cerr << "Exception occurred: " << e.what() << std::endl;
retval = -1;
}
try {
} catch (const std::logic_error &e) {
std::cerr << "Exception occurred: " << e.what() << std::endl;
retval = -2;
}
return retval;
}
Persistent string container with std::basic_string compatible interface.
Definition: basic_string.hpp:48
const CharT * c_str() const noexcept
Definition: basic_string.hpp:3687
Persistent memory aware implementation of Intel TBB concurrent_hash_map
Definition: concurrent_hash_map.hpp:1633
Persistent pointer class.
Definition: persistent_ptr.hpp:153
void close()
Closes the pool.
Definition: pool.hpp:256
PMEMobj pool class.
Definition: pool.hpp:482
persistent_ptr< T > root()
Retrieves pool's root object.
Definition: pool.hpp:644
String typedefs for common character types.