Games Task Scheduler (GTS)
A multi-processor scheduling framework for games engines
gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator > Class Template Reference

A parallel hash table. Properties: More...

#include <ParallelHashTable.h>

Inherits gts::AlignedAllocator< GTS_NO_SHARING_CACHE_LINE_SIZE >.

Classes

class  const_iterator
 A constant forward iterator. Read-locks the referenced element until destruction. More...
 
class  iterator
 A forward iterator. Write-locks the referenced element until destruction. More...
 

Public Types

using key_type = TKey
 
using mapped_type = TValue
 
using value_type = KeyValue< key_type, mapped_type >
 
using size_type = size_t
 
using allocator_type = TAllocator
 
using hasher_type = THasher
 

Public Member Functions

 ParallelHashTable (allocator_type const &allocator=allocator_type())
 
 ParallelHashTable (uint32_t growMutexCount, allocator_type const &allocator=allocator_type())
 
 ~ParallelHashTable ()
 
 ParallelHashTable (ParallelHashTable const &other)
 
 ParallelHashTable (ParallelHashTable &&other)
 
ParallelHashTableoperator= (ParallelHashTable const &other)
 
ParallelHashTableoperator= (ParallelHashTable &&other)
 
const_iterator cbegin () const
 
const_iterator cend () const
 
iterator begin () const
 
iterator end () const
 
size_type capacity () const
 
size_type max_size () const
 
const_iterator cfind (key_type const &key) const
 
iterator find (key_type const &key)
 
allocator_type get_allocator () const
 Get this Vectors allocator. More...
 
void reserve (size_type sizePow2)
 
iterator insert (value_type const &keyVal)
 
iterator insert (value_type &&keyVal)
 
iterator insert_or_assign (value_type const &keyVal)
 
iterator insert_or_assign (value_type &&keyVal)
 
size_type erase (key_type const &key)
 
iterator erase (iterator &iter)
 
void cleanup ()
 
void clear ()
 
template<bool useLocks>
bool _tryGrowTable (table_type *pTable, size_type sizePow2)
 
template<bool useLocks, typename... TArgs>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::iterator _tryInsert (bool assignable, TKey const &key, TArgs &&... args)
 
template<bool useLocks, typename... TArgs>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::ProbeResult _probeWrite (bool assignable, TKey const &key, table_type *&pOutTable, size_type &outIndex)
 

Friends

class base_const_iterator
 
class const_iterator
 
class iterator
 

Detailed Description

template<typename TKey, typename TValue, typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
class gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >

A parallel hash table. Properties:

  • Open addressing with O(log2*log2(N)) probing.
  • Grows when insert probing reaches O(log2*log2(N)).
  • Unbound.
  • Reference Stable.
  • Linearizable if Mutexes are fair.
    Remarks
  • User is responsible for calling cleanup() to delete stale tables after table doubling.
  • Iterators contain spin-locks that are unlocked in the iterator's destructor. Holding onto them may cause deadlocks.
  • Bad hash functions may cause huge tables. Author has only had this occur with pathologically bad hashers.
    Template Parameters
    TKeyThe unique identifier associated with each element in the container.
    TValueThe element type stored in the container.
    THasherThe hashing function.
    TAccessorSharedMutexThe shared mutex type to access individual elements.
    TGrowSharedMutexThe shared mutex type used to grow the table.
    TAllocatorThe allocator used by the storage backing.
    Todo:

    Try Robin Hood hashing. (Lock cost may be prohibitive.)

    Try Striped locks to protect slots instead of locks per slot.

    Try 1-byte per table entry trick and use SSE for compares. (see Skarupke and Kulukundis)

    Implement table shrinking.

    Divise a reclaimation system (hazard pointers etc.) the avoid the need for cleanup().

    Add equals template parameter.

    Add size() and empty()? requires shared counter :(

Constructor & Destructor Documentation

◆ ParallelHashTable() [1/4]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::ParallelHashTable ( allocator_type const &  allocator = allocator_type())
explicit

Constructs an empty container with the given 'allocator'. The number of striped mutexes is equal to the number of HW threads on the machine.

◆ ParallelHashTable() [2/4]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::ParallelHashTable ( uint32_t  growMutexCount,
allocator_type const &  allocator = allocator_type() 
)
explicit

Constructs an empty container with 'growMutexCount' striped mutexes and with the given 'allocator'.

◆ ~ParallelHashTable()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable::~ParallelHashTable ( )

Destructs the container. The destructors of the elements are called and the used storage is deallocated.

◆ ParallelHashTable() [3/4]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable::ParallelHashTable ( ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator > const &  other)

Copy constructor. Constructs the container with the copy of the contents of 'other'.

Remarks
Thread safe.

◆ ParallelHashTable() [4/4]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable::ParallelHashTable ( ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator > &&  other)

Move constructor. Constructs the container with the contents of other using move semantics. After the move, other is invalid.

Remarks
Thread safe.

Member Function Documentation

◆ begin()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::iterator ParallelHashTable::begin ( ) const
Returns
An iterator to the beginning of the current hash table state.
Remarks
Thread safe.

◆ capacity()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::size_type ParallelHashTable::capacity ( ) const
Returns
The total number of slots in the table.
Remarks
Thread safe.

◆ cbegin()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::const_iterator ParallelHashTable::cbegin ( ) const
Returns
An iterator to the beginning of the current hash table state.
Remarks
Thread safe.

◆ cend()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::const_iterator ParallelHashTable::cend ( ) const
Returns
An iterator to the end of the current hash table state.
Remarks
Thread safe.

◆ cfind()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::const_iterator ParallelHashTable::cfind ( key_type const &  key) const

Finds an element with key equivalent to 'key'.

Returns
A const_terator to an element with key equivalent to key. If no such element is found, past-the-end (see end()) iterator is returned.
Remarks
Thread safe.

◆ cleanup()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
void ParallelHashTable::cleanup ( )

Destroys old table memory.

Remarks
Not thread-safe.

◆ clear()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
void ParallelHashTable::clear ( )

Clear the data from the table.

Remarks
Not thread-safe.

◆ end()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::iterator ParallelHashTable::end ( ) const
Returns
An iterator to the end of the current hash table state.
Remarks
Thread safe.

◆ erase() [1/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::iterator ParallelHashTable::erase ( iterator iter)

Erases the element referenced by 'iter' and invalidates 'iter'.

Returns
The next iterator.
Remarks
Thread safe.

◆ erase() [2/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
size_type gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::erase ( key_type const &  key)

Tries to erase an element from the table.

Returns
The number if element erased.
Remarks
Thread safe.

◆ find()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::iterator ParallelHashTable::find ( key_type const &  key)

Finds an element with key equivalent to 'key'.

Returns
An iterator to an element with key equivalent to key. If no such element is found, past-the-end (see end()) iterator is returned.
Remarks
Thread safe.

◆ get_allocator()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::allocator_type ParallelHashTable::get_allocator ( ) const

Get this Vectors allocator.

Returns
The allocator.

◆ insert() [1/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
iterator gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::insert ( value_type &&  keyVal)

Inserts an element into the container, if the container doesn't already contain an element with an equivalent key.

Returns
An iterator to the element if it's inserted successfully, or past-the-end (see end()) iterator if: (1) The key already exists. (2) The table failed to grow.
Remarks
Thread safe.

◆ insert() [2/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
iterator gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::insert ( value_type const &  keyVal)

Inserts an element into the container, if the container doesn't already contain an element with an equivalent key.

Returns
An iterator to the element if it's inserted successfully, or past-the-end (see end()) iterator if: (1) The key already exists. (2) The table failed to grow.
Remarks
Thread safe.

◆ insert_or_assign() [1/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
iterator gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::insert_or_assign ( value_type &&  keyVal)

Inserts an element into the container or assign the new value if it already exists.

Returns
An iterator to the element if it's inserted successfully, or past-the-end (see end()) iterator if the table failed to grow.
Remarks
Thread safe.

◆ insert_or_assign() [2/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
iterator gts::ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::insert_or_assign ( value_type const &  keyVal)

Inserts an element into the container or assign the new value if it already exists.

Returns
An iterator to the element if it's inserted successfully, or past-the-end (see end()) iterator if the table failed to grow.
Remarks
Thread safe.

◆ max_size()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator >::size_type ParallelHashTable::max_size ( ) const
Returns
Maximum number of slots the container can grow to.
Remarks
Thread safe.

◆ operator=() [1/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator > & ParallelHashTable::operator= ( ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator > &&  other)

Move assignment operator. Replaces the contents with those of other using move semantics. After the move, other is invalid.

Remarks
Thread safe.

◆ operator=() [2/2]

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator > & ParallelHashTable::operator= ( ParallelHashTable< TKey, TValue, THasher, TAccessorSharedMutex, TGrowSharedMutex, TAllocator > const &  other)

Copy assignment operator. Replaces the contents with a copy of the contents of 'other'.

Remarks
Thread safe.

◆ reserve()

template<typename TKey , typename TValue , typename THasher = MurmurHash<TKey>, typename TAccessorSharedMutex = UnfairSharedSpinMutex<>, typename TGrowSharedMutex = UnfairSharedSpinMutex<>, typename TAllocator = AlignedAllocator<GTS_NO_SHARING_CACHE_LINE_SIZE>>
void ParallelHashTable::reserve ( size_type  sizePow2)

Increases the capacity of the hash table. Does nothing if 'sizePow2' < capacity.

Remarks
Thread-safe.