From a4ee99d4110c90ce6cf2245d369806903c1288e6 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Mon, 25 Jul 2022 23:15:46 -0400 Subject: [PATCH] Update doc for parallel hashmap extended APIs --- docs/phmap.md | 48 ++++++++++++++++++++++++------- examples/phmap/lazy_emplace_l.cpp | 6 ++-- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/docs/phmap.md b/docs/phmap.md index f22c01b..53f869a 100644 --- a/docs/phmap.md +++ b/docs/phmap.md @@ -51,7 +51,8 @@ The rules are the same as for `std::unordered_map`, and are valid for all the pa When the *parallel* containers are created with a mutex template parameter, such as [std::mutex](https://en.cppreference.com/w/cpp/thread/mutex), each submap is created with its own mutex, and internal operations such as `erase` are protected by the mutex of the target submap. -However, standard apis like `find()` returns an iterator, or `operator[]()` returns a reference, and these are not usable in a multithreaded environment under the internal lock protection. For this reason, the *parallel* containers provide extended APIs built with lambda, allowing to execute user code under the internal mutex protection. These APIs are as follows: +However, standard apis like `find()` returns an iterator, or `operator[]()` returns a reference, and these are not usable in a multithreaded environment under the internal lock protection. For this reason, the *parallel* containers provide extended APIs with callback functions, allowing to execute user code under the internal mutex protection. These APIs are as follows: + #### `if_contains` @@ -65,7 +66,10 @@ bool if_contains(const key_arg& key, F&& f) const; example: ```c++ - using Map = ThisMap; + using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, + 4, std::mutex>; Map m = { {1, 7}, {2, 9} }; auto val = 0; @@ -75,6 +79,7 @@ example: ``` + #### `modify_if` if the container contains the provided key, the lambda is called with the `value_type` (under write lock protection) and `modify_if` returns `true`. This is a non-const API and the lambda is allowed to modify the mapped value. @@ -87,7 +92,10 @@ bool modify_if(const key_arg& key, F&& f); example: ```c++ - using Map = ThisMap; + using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, + 4, std::mutex>; Map m = { {1, 7}, {2, 9} }; auto set_value = [](Map::value_type& v) { v.second = 11; }; @@ -95,6 +103,7 @@ example: m.modify_if(3, set_value); // returns false, because m[3] does not exist ``` + #### `erase_if` if the container contains the provided key, the lambda is called with the `value_type` (under write lock protection). If the lambda returns `true`, the key is subsequently erased from the map (the write lock is only released after erase). Returns `true` if key was erased, `false` otherwise. @@ -107,7 +116,10 @@ bool erase_if(const key_arg& key, F&& f); example: ```c++ - using Map = ThisMap; + using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, + 4, std::mutex>; Map m = { {1, 7}, {2, 9}, {5, 6} }; assert(m.erase_if(9, [](Map::value_type& v) { assert(0); return v.second == 12; }) == false); // m[9] not present - lambda not called @@ -116,9 +128,10 @@ example: assert(m.erase_if(5, [](Map::value_type& v) { return v.second == 6; }) == true); // lambda returns true, so m[5] erased ``` + #### `try_emplace_l` -if the container does not contains the provided key, it is inserted and the mapped value is value-constructed with the provided arguments (if any), as with `try_emplace`. If the container already contains the provided key, then the lambda is called with the `value_type` (under write lock protection) and can update the mapped value. Returns` true` if the key was not already present, `false` otherwise. +if the container does not contains the provided key, it is inserted and the mapped value is value-constructed with the provided arguments (if any), as with `try_emplace`. If the container already contains the provided key, then the lambda is called with the `value_type` (under write lock protection) and can update the mapped value. Returns `true` if the key was not already present, `false` otherwise. ```c++ template @@ -128,7 +141,10 @@ bool try_emplace_l(K&& k, F&& f, Args&&... args); example: ```c++ - using Map = ThisMap; + using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, + 4, std::mutex>; Map m = { {1, 7}, {2, 9} }; // overwrite an existing value @@ -148,9 +164,10 @@ example: assert(m[4] == 999); ``` + #### `lazy_emplace_l` -if the container already contains the provided key, the first lambda is called with the `value_type` (under write lock protection) and can update the mapped value. if the container does not contains the provided key, the second lambda is called and it should invoke the passed constructor to construct the value. Returns true if key was not already present, false otherwise. +If the container already contains the provided key, the first lambda is called with the `value_type` (under write lock protection) and can update the mapped value. if the container does not contains the provided key, the second lambda is called (under write lock protection as well) and it should invoke the passed constructor to construct the value. Returns `true` if key was not already present, `false` otherwise. ```c++ template @@ -159,7 +176,10 @@ bool lazy_emplace_l(const key_arg& key, FExists&& fExists, FEmplace&& fEmplac example: ```c++ - using Map = ThisMap; + using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, + 4, std::mutex>; Map m = { {1, 7}, {2, 9} }; // insert a value that is not already present. @@ -176,6 +196,7 @@ example: assert(m[5] == 6); ``` + #### `with_submap`/ `with_submap_m` Access internal submaps by index (under lock protection). @@ -192,7 +213,10 @@ void with_submap_m(size_t idx, F&& fCallback); // non-const version example: ```c++ - using Map = ThisMap; + using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, + 4, std::mutex>; Map m = { {1, 7}, {2, 8}, {5, 11} }; int counter = 0; for (size_t i=0; i; + using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, + 4, std::mutex>; Map m = { {1, 7}, {2, 8}, {5, 11} }; // increment all values by 1 diff --git a/examples/phmap/lazy_emplace_l.cpp b/examples/phmap/lazy_emplace_l.cpp index 9991d51..7f91cac 100644 --- a/examples/phmap/lazy_emplace_l.cpp +++ b/examples/phmap/lazy_emplace_l.cpp @@ -17,9 +17,9 @@ class srwlock { void unlock() { ReleaseSRWLockExclusive(&_lock); } }; -using Map = gtl::parallel_flat_hash_map, - gtl::priv::hash_default_eq, - std::allocator>, 8, srwlock>; +using Map = gtl::parallel_flat_hash_map, + gtl::priv::hash_default_eq, + std::allocator>, 8, srwlock>; class Dict {