proxygen
|
#include <AtomicUnorderedMap.h>
Classes | |
struct | ConstIterator |
struct | Slot |
Public Types | |
typedef Key | key_type |
typedef Value | mapped_type |
typedef std::pair< Key, Value > | value_type |
typedef std::size_t | size_type |
typedef std::ptrdiff_t | difference_type |
typedef Hash | hasher |
typedef KeyEqual | key_equal |
typedef const value_type & | const_reference |
typedef struct folly::AtomicUnorderedInsertMap::ConstIterator | const_iterator |
Public Member Functions | |
AtomicUnorderedInsertMap (size_t maxSize, float maxLoadFactor=0.8f, const Allocator &alloc=Allocator()) | |
~AtomicUnorderedInsertMap () | |
template<typename Func > | |
std::pair< const_iterator, bool > | findOrConstruct (const Key &key, Func &&func) |
template<class K , class V > | |
std::pair< const_iterator, bool > | emplace (const K &key, V &&value) |
const_iterator | find (const Key &key) const |
const_iterator | cbegin () const |
const_iterator | cend () const |
Public Attributes | |
friend | ConstIterator |
Private Types | |
enum | : IndexType { kMaxAllocationTries = 1000 } |
enum | BucketState : IndexType { EMPTY = 0, CONSTRUCTING = 1, LINKED = 2 } |
Private Member Functions | |
IndexType | keyToSlotIdx (const Key &key) const |
IndexType | find (const Key &key, IndexType slot) const |
IndexType | allocateNear (IndexType start) |
IndexType | allocationAttempt (IndexType start, IndexType tries) const |
void | zeroFillSlots () |
Private Attributes | |
size_t | mmapRequested_ |
size_t | numSlots_ |
size_t | slotMask_ |
tricky, see keyToSlodIdx More... | |
Allocator | allocator_ |
Slot * | slots_ |
You're probably reading this because you are looking for an AtomicUnorderedMap<K,V> that is fully general, highly concurrent (for reads, writes, and iteration), and makes no performance compromises. We haven't figured that one out yet. What you will find here is a hash table implementation that sacrifices generality so that it can give you all of the other things.
LIMITATIONS:
WHAT YOU GET IN EXCHANGE:
COMMENTS ON INSERT-ONLY
This map provides wait-free linearizable reads and lock-free linearizable inserts. Inserted values won't be moved, but no concurrency control is provided for safely updating them. To remind you of that fact they are only provided in const form. This is the only simple safe thing to do while preserving something like the normal std::map iteration form, which requires that iteration be exposed via std::pair (and prevents encapsulation of access to the value).
There are a couple of reasonable policies for doing in-place concurrency control on the values. I am hoping that the policy can be injected via the value type or an extra template param, to keep the core AtomicUnorderedInsertMap insert-only:
CONST: this is the currently implemented strategy, which is simple, performant, and not that expressive. You can always put in a value with a mutable field (see MutableAtom below), but that doesn't look as pretty as it should.
ATOMIC: for integers and integer-size trivially copyable structs (via an adapter like tao/queues/AtomicStruct) the value can be a std::atomic and read and written atomically.
SEQ-LOCK: attach a counter incremented before and after write. Writers serialize by using CAS to make an even->odd transition, then odd->even after the write. Readers grab the value with memcpy, checking sequence value before and after. Readers retry until they see an even sequence number that doesn't change. This works for larger structs, but still requires memcpy to be equivalent to copy assignment, and it is no longer lock-free. It scales very well, because the readers are still invisible (no cache line writes).
LOCK: folly's SharedMutex would be a good choice here.
MEMORY ALLOCATION
Underlying memory is allocated as a big anonymous mmap chunk, which might be cheaper than calloc() and is certainly not more expensive for large maps. If the SkipKeyValueDeletion template param is true then deletion of the map consists of unmapping the backing memory, which is much faster than destructing all of the keys and values. Feel free to override if std::is_trivial_destructor isn't recognizing the triviality of your destructors.
Definition at line 144 of file AtomicUnorderedMap.h.
typedef struct folly::AtomicUnorderedInsertMap::ConstIterator folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::const_iterator |
typedef const value_type& folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::const_reference |
Definition at line 152 of file AtomicUnorderedMap.h.
typedef std::ptrdiff_t folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::difference_type |
Definition at line 149 of file AtomicUnorderedMap.h.
typedef Hash folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::hasher |
Definition at line 150 of file AtomicUnorderedMap.h.
typedef KeyEqual folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::key_equal |
Definition at line 151 of file AtomicUnorderedMap.h.
typedef Key folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::key_type |
Definition at line 145 of file AtomicUnorderedMap.h.
typedef Value folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::mapped_type |
Definition at line 146 of file AtomicUnorderedMap.h.
typedef std::size_t folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::size_type |
Definition at line 148 of file AtomicUnorderedMap.h.
typedef std::pair<Key, Value> folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::value_type |
Definition at line 147 of file AtomicUnorderedMap.h.
|
private |
|
private |
|
inlineexplicit |
Constructs a map that will support the insertion of maxSize key-value pairs without exceeding the max load factor. Load factors of greater than 1 are not supported, and once the actual load factor of the map approaches 1 the insert performance will suffer. The capacity is limited to 2^30 (about a billion) for the default IndexType, beyond which we will throw invalid_argument.
Definition at line 207 of file AtomicUnorderedMap.h.
References f, min, and folly::nextPowTwo().
|
inline |
Definition at line 234 of file AtomicUnorderedMap.h.
References i.
|
inlineprivate |
Allocates a slot and returns its index. Tries to put it near slots_[start].
Definition at line 432 of file AtomicUnorderedMap.h.
|
inlineprivate |
Returns the slot we should attempt to allocate after tries failed tries, starting from the specified slot. This is pulled out so we can specialize it differently during deterministic testing
Definition at line 448 of file AtomicUnorderedMap.h.
References LIKELY, folly::Random::rand32(), and folly::Random::rand64().
|
inline |
Definition at line 324 of file AtomicUnorderedMap.h.
|
inline |
Definition at line 332 of file AtomicUnorderedMap.h.
Referenced by TEST().
|
inline |
This isn't really emplace, but it is what we need to test. Eventually we can duplicate all of the std::pair constructor forms, including a recursive tuple forwarding template http://functionalcpp.wordpress.com/2013/08/28/tuple-forwarding/).
Definition at line 315 of file AtomicUnorderedMap.h.
References folly::value(), and testing::Value().
Referenced by BENCHMARK(), TEST(), and TYPED_TEST().
|
inline |
Definition at line 320 of file AtomicUnorderedMap.h.
Referenced by BENCHMARK(), TEST(), and TYPED_TEST().
|
inlineprivate |
Definition at line 419 of file AtomicUnorderedMap.h.
References folly::gen::first, folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::Slot::headAndState_, and folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::Slot::next_.
|
inline |
Searches for the key, returning (iter,false) if it is found. If it is not found calls the functor Func with a void* argument that is raw storage suitable for placement construction of a Value (see raw_value_type), then returns (iter,true). May call Func and then return (iter,false) if there are other concurrent writes, in which case the newly constructed value will be immediately destroyed.
This function does not block other readers or writers. If there are other concurrent writes, many parallel calls to func may happen and only the first one to complete will win. The values constructed by the other calls to func will be destroyed.
Usage:
AtomicUnorderedInsertMap<std::string,std::string> memo;
auto value = memo.findOrConstruct(key, [=](void* raw) { new (raw) std::string(computation(key)); })->first;
Definition at line 263 of file AtomicUnorderedMap.h.
References testing::Key().
|
inlineprivate |
Definition at line 410 of file AtomicUnorderedMap.h.
References h.
|
inlineprivate |
Definition at line 463 of file AtomicUnorderedMap.h.
References Atom, testing::Key(), uint64_t, value, and testing::Value().
|
private |
Definition at line 407 of file AtomicUnorderedMap.h.
friend folly::AtomicUnorderedInsertMap< Key, Value, Hash, KeyEqual, SkipKeyValueDeletion, Atom, IndexType, Allocator >::ConstIterator |
Definition at line 199 of file AtomicUnorderedMap.h.
|
private |
Definition at line 401 of file AtomicUnorderedMap.h.
|
private |
Definition at line 402 of file AtomicUnorderedMap.h.
|
private |
tricky, see keyToSlodIdx
Definition at line 405 of file AtomicUnorderedMap.h.
|
private |
Definition at line 408 of file AtomicUnorderedMap.h.