diff --git a/README.md b/README.md index 72166c1..a963b41 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,84 @@ This is already looking pretty good. For large hash_maps, the parallel_flat_hash But there is another aspect of the inherent parallelism of the parallel_hash_map which is interesting to explore. As we know, typical hash maps cannot be modified from multiple threads without explicit synchronization. And bracketing write accesses to a shared hash_map with synchronization primitives, such as mutexes, can reduce the concurrency of our program, and even cause deadlocks. -Because the parallel_hash_map is built of sixteen separate subtables, it posesses some intrinsic parallelism. Indeed, suppose you can make sure that different threads will use different subtables, you would be able to insert +Because the parallel_hash_map is built of sixteen separate subtables, it posesses some intrinsic parallelism. Indeed, suppose you can make sure that different threads will use different subtables, you would be able to insert into the same parallel_hash_map at the same time from the different threads without any locking. + +So, if you can iterate over the values you want to insert into the hash table, the idea is that each thread will iterate over all values, and then for each value: + +1. compute the hash for that value +2. compute the subtable index for that hash +3. if the subtable index is the assigned to this thread, do nothing and continue to the next value, otherwise insert the value + +Here is the code for the single-threaded insert: + +```c++ +template +void _fill_random_inner(int64_t cnt, HT &hash, RSU &rsu) +{ + for (int64_t i=0; i +struct TD +{ + int64_t thread_idx; + int64_t num_threads; + int64_t cnt; + HT &hash; + RSU rsu; // generates a random sequence of unique integers +}; + +// -------------------------------------------------------------------------- +template +void _fill_random_inner_thr(TD td) +{ + typename HT::hasher hasher; // get hasher object from the hash table + size_t modulo = td.hash.subcnt() / td.num_threads; // subcnt() returns the number of subtables + + for (int64_t i=0; i +void _fill_random_inner_mt(int64_t cnt, HT &hash, RSU &rsu) +{ + constexpr int64_t num_threads = 8; // has to be a power of two + std::unique_ptr threads[num_threads]; + + for (int64_t i=0; i td {i, num_threads, cnt, hash, rsu}; + threads[i].reset(new std::thread(_fill_random_inner_thr, td)); + } + + // rsu passed by value to threads... we need to increment the reference object + for (int64_t i=0; ijoin(); +} +``` + diff --git a/bench.cc b/bench.cc index a00ed25..bd4300d 100644 --- a/bench.cc +++ b/bench.cc @@ -58,7 +58,7 @@ private: // -------------------------------------------------------------------------- // from: https://github.com/preshing/RandomSequence // -------------------------------------------------------------------------- -class RandomSequenceOfUnique +class RSU { private: unsigned int m_index; @@ -74,7 +74,7 @@ private: } public: - RandomSequenceOfUnique(unsigned int seedBase, unsigned int seedOffset) + RSU(unsigned int seedBase, unsigned int seedOffset) { m_index = permuteQPR(permuteQPR(seedBase) + 0x682f0161); m_intermediateOffset = permuteQPR(permuteQPR(seedOffset) + 0x46790905); @@ -146,7 +146,7 @@ static const char *test = "random"; // -------------------------------------------------------------------------- template -void _fill_random_inner(int64_t cnt, HT &hash, RandomSequenceOfUnique &rsu) +void _fill_random_inner(int64_t cnt, HT &hash, RSU &rsu) { for (int64_t i=0; i void _fill_random_inner_thr(TD td) { #ifdef MT_SUPPORT - typename HT::hasher hasher; - size_t modulo = td.hash.subcnt() / td.num_threads; - for (int64_t i=0; i td) // -------------------------------------------------------------------------- template -void _fill_random_inner_mt(int64_t cnt, HT &hash, RandomSequenceOfUnique &rsu) +void _fill_random_inner_mt(int64_t cnt, HT &hash, RSU &rsu) { - constexpr int64_t num_threads = 8; + constexpr int64_t num_threads = 8; // has to be a power of two std::unique_ptr threads[num_threads]; for (int64_t i=0; i td {i, num_threads, cnt, hash, rsu}; - assert(&td.hash == &hash); threads[i].reset(new std::thread(_fill_random_inner_thr, td)); } + + // rsu passed by value to threads... we need to increment the reference object for (int64_t i=0; i