diff --git a/.clang-tidy b/.clang-tidy index 240653fb..a48405f2 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,20 +1,18 @@ --- -Checks: '*, - - -altera*, - -fuchsia*, - -llvmlibc*, - - -bugprone-easily-swappable-parameters, - -cert-err58-cpp, - -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-pro-bounds-constant-array-index, - -cppcoreguidelines-pro-bounds-pointer-arithmetic, - -llvm-header-guard, - -readability-function-cognitive-complexity, - -readability-identifier-length, - -readability-magic-numbers, - ' +Checks: '* + -altera* + -bugprone-easily-swappable-parameters + -cert-err58-cpp + -cppcoreguidelines-avoid-magic-numbers + -cppcoreguidelines-pro-bounds-constant-array-index + -cppcoreguidelines-pro-bounds-pointer-arithmetic + -fuchsia* + -llvm-header-guard + -llvmlibc* + -readability-function-cognitive-complexity + -readability-identifier-length + -readability-magic-numbers + ' WarningsAsErrors: '*' HeaderFilterRegex: '' CheckOptions: diff --git a/CMakeLists.txt b/CMakeLists.txt index a72b1329..7213ef67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12) project("unordered_dense" - VERSION 3.1.1 + VERSION 4.0.0 DESCRIPTION "A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion" HOMEPAGE_URL "https://github.com/martinus/unordered_dense") diff --git a/README.md b/README.md index 6c2b05f0..69f71425 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. -The classes `ankerl::unordered_dense::map` and `ankerl::unordered_dense::set` are (almost) drop-in replacements of `std::unordered_map` and `std::unordered_set`. While they don't have as strong iterator / reference stability guaranties, they are typically *much* faster. +The classes `ankerl::unordered_dense::map` and `ankerl::unordered_dense::set` are (almost) drop-in replacements of `std::unordered_map` and `std::unordered_set`. While they don't have as strong iterator / reference stability guaranties, they are typically *much* faster. + +Additionally, there are `ankerl::unordered_dense::segmented_map` and `ankerl::unordered_dense::segmented_set` with lower peak memory usage. - [1. Overview](#1-overview) - [2. Installation](#2-installation) @@ -257,13 +259,29 @@ The map/set supports two different bucket types. The default should be good for * up to 2^63 = 9223372036854775808 elements. * 12 bytes overhead per bucket. -## 4. Design +## 4. `segmented_map` and `segmented_set` + +`ankerl::unordered_dense` provides a custom container implementation that has lower memory requirements than the default `std::vector`. Memory is not contiguous, but it can allocate segments without having to reallocate and move all the elements. In summary, this leads to + +* Much smoother memory usage, memory usage increases continuously. +* No high peak memory usage. +* Faster insertion because elements never need to be moved to new allocated blocks +* Slightly slower indexing compared to `std::vector` because an additional indirection is needed. + +Here is a comparison against `absl::flat_hash_map` and the `ankerl::unordered_dense::map` when inserting 10 million entries +![allocated memory](doc/allocated_memory.png) + +Abseil is fastest for this simple inserting test, taking a bit over 0.8 seconds. It's peak memory usage is about 430 MB. Note how the memory usage goes down after the last peak; when it goes down to ~290MB it has finished rehashing and could free the previously used memory block. + +`ankerl::unordered_dense::segmented_map` doesn't have these peaks, and instead has a smooth increase of memory usage. Note there are still sudden drops & increases in memory because the indexing data structure needs still needs to increase by a fixed factor. But due to holding the data in a separate container we are able to first free the old data structure, and then allocate a new, bigger indexing structure; thus we do not have peaks. + +## 5. Design The map/set has two data structures: * `std::vector` which holds all data. map/set iterators are just `std::vector::iterator`! * An indexing structure (bucket array), which is a flat array with 8-byte buckets. -### 4.1. Inserts +### 5.1. Inserts Whenever an element is added it is `emplace_back` to the vector. The key is hashed, and an entry (bucket) is added at the corresponding location in the bucket array. The bucket has this structure: @@ -283,12 +301,12 @@ Each bucket stores 3 things: This structure is especially designed for the collision resolution strategy robin-hood hashing with backward shift deletion. -### 4.2. Lookups +### 5.2. Lookups The key is hashed and the bucket array is searched if it has an entry at that location with that fingerprint. When found, the key in the data vector is compared, and when equal the value is returned. -### 4.3. Removals +### 5.3. Removals Since all data is stored in a vector, removals are a bit more complicated: diff --git a/data/fuzz/api/00b05d7d90848f32ca139aa1a2a7bfc05ecdc6aa b/data/fuzz/api/00b05d7d90848f32ca139aa1a2a7bfc05ecdc6aa new file mode 100644 index 00000000..0dee64d4 --- /dev/null +++ b/data/fuzz/api/00b05d7d90848f32ca139aa1a2a7bfc05ecdc6aa @@ -0,0 +1 @@ +5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555=555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 \ No newline at end of file diff --git a/data/fuzz/api/00d29941d07ea0f8bd175bf354d84c025f1aafb4 b/data/fuzz/api/00d29941d07ea0f8bd175bf354d84c025f1aafb4 new file mode 100644 index 00000000..d63d605f Binary files /dev/null and b/data/fuzz/api/00d29941d07ea0f8bd175bf354d84c025f1aafb4 differ diff --git a/data/fuzz/api/05cb85ee3c7ee6f363d64e4e3e2dd99a9c755a0a b/data/fuzz/api/05cb85ee3c7ee6f363d64e4e3e2dd99a9c755a0a new file mode 100644 index 00000000..e7c36b99 --- /dev/null +++ b/data/fuzz/api/05cb85ee3c7ee6f363d64e4e3e2dd99a9c755a0a @@ -0,0 +1 @@ +@UU]U]U \ No newline at end of file diff --git a/data/fuzz/api/08c684aa36bebe1d5c53e9761e6029a490377e93 b/data/fuzz/api/08c684aa36bebe1d5c53e9761e6029a490377e93 new file mode 100644 index 00000000..883e7aa7 --- /dev/null +++ b/data/fuzz/api/08c684aa36bebe1d5c53e9761e6029a490377e93 @@ -0,0 +1 @@ +hhhh \ No newline at end of file diff --git a/data/fuzz/api/0d06b0a171fca96ca6a3e68323726b39e008838a b/data/fuzz/api/0d06b0a171fca96ca6a3e68323726b39e008838a new file mode 100644 index 00000000..e440645c --- /dev/null +++ b/data/fuzz/api/0d06b0a171fca96ca6a3e68323726b39e008838a @@ -0,0 +1 @@ +#H \ No newline at end of file diff --git a/data/fuzz/api/1078aadb2db22cf008832242292a02fca407acb2 b/data/fuzz/api/1078aadb2db22cf008832242292a02fca407acb2 new file mode 100644 index 00000000..294a0ceb --- /dev/null +++ b/data/fuzz/api/1078aadb2db22cf008832242292a02fca407acb2 @@ -0,0 +1 @@ + C \ No newline at end of file diff --git a/data/fuzz/api/13c116fbc8b8bc27f22b1b4ff941a85281ada1a3 b/data/fuzz/api/13c116fbc8b8bc27f22b1b4ff941a85281ada1a3 new file mode 100644 index 00000000..c45c26c2 Binary files /dev/null and b/data/fuzz/api/13c116fbc8b8bc27f22b1b4ff941a85281ada1a3 differ diff --git a/data/fuzz/api/14be94de8a37175291675a2ca5340cb7b0adb77d b/data/fuzz/api/14be94de8a37175291675a2ca5340cb7b0adb77d new file mode 100644 index 00000000..98acecc6 Binary files /dev/null and b/data/fuzz/api/14be94de8a37175291675a2ca5340cb7b0adb77d differ diff --git a/data/fuzz/api/14c23f102a05b1907a103d5e9d2ac49b6bd1e706 b/data/fuzz/api/14c23f102a05b1907a103d5e9d2ac49b6bd1e706 new file mode 100644 index 00000000..c8fb3bd9 Binary files /dev/null and b/data/fuzz/api/14c23f102a05b1907a103d5e9d2ac49b6bd1e706 differ diff --git a/data/fuzz/api/17b75617b5c0ab200b6bd4275e8c19dda4aea490 b/data/fuzz/api/17b75617b5c0ab200b6bd4275e8c19dda4aea490 new file mode 100644 index 00000000..c4567b40 Binary files /dev/null and b/data/fuzz/api/17b75617b5c0ab200b6bd4275e8c19dda4aea490 differ diff --git a/data/fuzz/api/17f14f7cba853ed70250861e9ce5896766de8bf9 b/data/fuzz/api/17f14f7cba853ed70250861e9ce5896766de8bf9 new file mode 100644 index 00000000..b9299d28 Binary files /dev/null and b/data/fuzz/api/17f14f7cba853ed70250861e9ce5896766de8bf9 differ diff --git a/data/fuzz/api/18c8e7b5836a3fd84896e6423e48f20cf6b5f7fe b/data/fuzz/api/18c8e7b5836a3fd84896e6423e48f20cf6b5f7fe new file mode 100644 index 00000000..a51bc727 --- /dev/null +++ b/data/fuzz/api/18c8e7b5836a3fd84896e6423e48f20cf6b5f7fe @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/fuzz/api/1b3fca8725ca7c29201949316cc5073b74e7b3fa b/data/fuzz/api/1b3fca8725ca7c29201949316cc5073b74e7b3fa new file mode 100644 index 00000000..8f1ba19f Binary files /dev/null and b/data/fuzz/api/1b3fca8725ca7c29201949316cc5073b74e7b3fa differ diff --git a/data/fuzz/api/1cb6ffd144b5e06280d3514159ff551aab64379e b/data/fuzz/api/1cb6ffd144b5e06280d3514159ff551aab64379e new file mode 100644 index 00000000..4b55263c Binary files /dev/null and b/data/fuzz/api/1cb6ffd144b5e06280d3514159ff551aab64379e differ diff --git a/data/fuzz/api/208287c8601b08c9740907fb03657392d883b542 b/data/fuzz/api/208287c8601b08c9740907fb03657392d883b542 new file mode 100644 index 00000000..b3f93f0f Binary files /dev/null and b/data/fuzz/api/208287c8601b08c9740907fb03657392d883b542 differ diff --git a/data/fuzz/api/29371b45b46460a6bf8240283a31cc49a30c6c83 b/data/fuzz/api/29371b45b46460a6bf8240283a31cc49a30c6c83 new file mode 100644 index 00000000..9b21d674 --- /dev/null +++ b/data/fuzz/api/29371b45b46460a6bf8240283a31cc49a30c6c83 @@ -0,0 +1 @@ +۱۱K۱V۱ \ No newline at end of file diff --git a/data/fuzz/api/294330edf83a589e3422af6b716dc54611b60547 b/data/fuzz/api/294330edf83a589e3422af6b716dc54611b60547 new file mode 100644 index 00000000..382fcfe0 Binary files /dev/null and b/data/fuzz/api/294330edf83a589e3422af6b716dc54611b60547 differ diff --git a/data/fuzz/api/2a012853642433f3129adbd28578c173afa8375d b/data/fuzz/api/2a012853642433f3129adbd28578c173afa8375d new file mode 100644 index 00000000..e0038053 Binary files /dev/null and b/data/fuzz/api/2a012853642433f3129adbd28578c173afa8375d differ diff --git a/data/fuzz/api/2a687445f01f304375eef92e7b616a56e6e2bb2d b/data/fuzz/api/2a687445f01f304375eef92e7b616a56e6e2bb2d new file mode 100644 index 00000000..13d2ceb8 --- /dev/null +++ b/data/fuzz/api/2a687445f01f304375eef92e7b616a56e6e2bb2d @@ -0,0 +1 @@ +{-[-[--[-[---[--[-[---[--[-[--[-//:{//[//z~////[/////[/![ \ No newline at end of file diff --git a/data/fuzz/api/31c440f9ab80976665056e180c9c49263c32aeb7 b/data/fuzz/api/31c440f9ab80976665056e180c9c49263c32aeb7 new file mode 100644 index 00000000..8ffac047 --- /dev/null +++ b/data/fuzz/api/31c440f9ab80976665056e180c9c49263c32aeb7 @@ -0,0 +1 @@ +/5z05z/ 00 0 Rz05 \ No newline at end of file diff --git a/data/fuzz/api/35b6e4e31e923348f16533f51f60777c7f6279c1 b/data/fuzz/api/35b6e4e31e923348f16533f51f60777c7f6279c1 new file mode 100644 index 00000000..e4e87f98 Binary files /dev/null and b/data/fuzz/api/35b6e4e31e923348f16533f51f60777c7f6279c1 differ diff --git a/data/fuzz/api/370d3923b73a01d1ede661de47df1593b46855b6 b/data/fuzz/api/370d3923b73a01d1ede661de47df1593b46855b6 new file mode 100644 index 00000000..a5b6dc4c Binary files /dev/null and b/data/fuzz/api/370d3923b73a01d1ede661de47df1593b46855b6 differ diff --git a/data/fuzz/api/3a559ebbfaf72f7a0663b2feecaf800dbb1a6588 b/data/fuzz/api/3a559ebbfaf72f7a0663b2feecaf800dbb1a6588 new file mode 100644 index 00000000..a03be2a7 Binary files /dev/null and b/data/fuzz/api/3a559ebbfaf72f7a0663b2feecaf800dbb1a6588 differ diff --git a/data/fuzz/api/3ad5741153a7b7191167b211585aca18963f9545 b/data/fuzz/api/3ad5741153a7b7191167b211585aca18963f9545 new file mode 100644 index 00000000..e99ce15a Binary files /dev/null and b/data/fuzz/api/3ad5741153a7b7191167b211585aca18963f9545 differ diff --git a/data/fuzz/api/3ee207f869c84fca99b82c26283935f5db0fbaf7 b/data/fuzz/api/3ee207f869c84fca99b82c26283935f5db0fbaf7 new file mode 100644 index 00000000..b67646e5 --- /dev/null +++ b/data/fuzz/api/3ee207f869c84fca99b82c26283935f5db0fbaf7 @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/data/fuzz/api/4067c8d1bee31f0fff1ad43cc9bf358bf965830d b/data/fuzz/api/4067c8d1bee31f0fff1ad43cc9bf358bf965830d new file mode 100644 index 00000000..b9f8ba54 --- /dev/null +++ b/data/fuzz/api/4067c8d1bee31f0fff1ad43cc9bf358bf965830d @@ -0,0 +1 @@ +/ /00 0 0 05 \ No newline at end of file diff --git a/data/fuzz/api/41d63d53691c41ce65840e87e46c4f6832678ed0 b/data/fuzz/api/41d63d53691c41ce65840e87e46c4f6832678ed0 new file mode 100644 index 00000000..7839fd9f Binary files /dev/null and b/data/fuzz/api/41d63d53691c41ce65840e87e46c4f6832678ed0 differ diff --git a/data/fuzz/api/451bb5b8e85859f406439d5777ac733416f4bf43 b/data/fuzz/api/451bb5b8e85859f406439d5777ac733416f4bf43 new file mode 100644 index 00000000..a2021efe --- /dev/null +++ b/data/fuzz/api/451bb5b8e85859f406439d5777ac733416f4bf43 @@ -0,0 +1 @@ +$JJJ \ No newline at end of file diff --git a/data/fuzz/api/46d6a22920746f08ce476c3c0891a312422d8430 b/data/fuzz/api/46d6a22920746f08ce476c3c0891a312422d8430 new file mode 100644 index 00000000..e59996f9 --- /dev/null +++ b/data/fuzz/api/46d6a22920746f08ce476c3c0891a312422d8430 @@ -0,0 +1 @@ +{//R[򲲲[򲲲[ \ No newline at end of file diff --git a/data/fuzz/api/46ed7c052afc7f78fad09e04bdd05e230bae9bf1 b/data/fuzz/api/46ed7c052afc7f78fad09e04bdd05e230bae9bf1 new file mode 100644 index 00000000..b795aeda --- /dev/null +++ b/data/fuzz/api/46ed7c052afc7f78fad09e04bdd05e230bae9bf1 @@ -0,0 +1 @@ +0000000000000000000000[000000000000,0000000000000000000000000000000000000000000000[000000000000000000000000000000000000000000000000000000000500000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020 \ No newline at end of file diff --git a/data/fuzz/api/4a0977214a607e3f61185afb43493a0a3dda0ab0 b/data/fuzz/api/4a0977214a607e3f61185afb43493a0a3dda0ab0 new file mode 100644 index 00000000..afa48f1d --- /dev/null +++ b/data/fuzz/api/4a0977214a607e3f61185afb43493a0a3dda0ab0 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/fuzz/api/4a9ffaa04518d730003e86d583c9c6fdcab4092e b/data/fuzz/api/4a9ffaa04518d730003e86d583c9c6fdcab4092e new file mode 100644 index 00000000..4047fd20 Binary files /dev/null and b/data/fuzz/api/4a9ffaa04518d730003e86d583c9c6fdcab4092e differ diff --git a/data/fuzz/api/4f886111f88e60d8bd31f01ad6d036da9f2936d5 b/data/fuzz/api/4f886111f88e60d8bd31f01ad6d036da9f2936d5 new file mode 100644 index 00000000..5f49fc9f Binary files /dev/null and b/data/fuzz/api/4f886111f88e60d8bd31f01ad6d036da9f2936d5 differ diff --git a/data/fuzz/api/5124a4a6f29061b3d61f3e8a86db67a4128ba351 b/data/fuzz/api/5124a4a6f29061b3d61f3e8a86db67a4128ba351 new file mode 100644 index 00000000..b82990af --- /dev/null +++ b/data/fuzz/api/5124a4a6f29061b3d61f3e8a86db67a4128ba351 @@ -0,0 +1 @@ +y<<[ \ No newline at end of file diff --git a/data/fuzz/api/51445c8fc21e47597603dac1a2e7dcec5c17b29b b/data/fuzz/api/51445c8fc21e47597603dac1a2e7dcec5c17b29b new file mode 100644 index 00000000..2378721f Binary files /dev/null and b/data/fuzz/api/51445c8fc21e47597603dac1a2e7dcec5c17b29b differ diff --git a/data/fuzz/api/52c276e9c1bfe6866cc237244f421f761a3067a5 b/data/fuzz/api/52c276e9c1bfe6866cc237244f421f761a3067a5 new file mode 100644 index 00000000..3fbbd4b0 Binary files /dev/null and b/data/fuzz/api/52c276e9c1bfe6866cc237244f421f761a3067a5 differ diff --git a/data/fuzz/api/5375cdb60501a7b4d522805d484d32ea8a0bc605 b/data/fuzz/api/5375cdb60501a7b4d522805d484d32ea8a0bc605 new file mode 100644 index 00000000..278e5da0 --- /dev/null +++ b/data/fuzz/api/5375cdb60501a7b4d522805d484d32ea8a0bc605 @@ -0,0 +1 @@ +@! \ No newline at end of file diff --git a/data/fuzz/api/53d3a3d0e9c50b3050f17f9cb74e345f8c6400e1 b/data/fuzz/api/53d3a3d0e9c50b3050f17f9cb74e345f8c6400e1 new file mode 100644 index 00000000..2f8e587e --- /dev/null +++ b/data/fuzz/api/53d3a3d0e9c50b3050f17f9cb74e345f8c6400e1 @@ -0,0 +1 @@ +$ \ No newline at end of file diff --git a/data/fuzz/api/56869e5a231807e4022a8ae6cc7ba14993db0f44 b/data/fuzz/api/56869e5a231807e4022a8ae6cc7ba14993db0f44 new file mode 100644 index 00000000..e5dd2bf4 Binary files /dev/null and b/data/fuzz/api/56869e5a231807e4022a8ae6cc7ba14993db0f44 differ diff --git a/data/fuzz/api/5aa45433beed76b16f367fe69a5f0f722e9785ff b/data/fuzz/api/5aa45433beed76b16f367fe69a5f0f722e9785ff new file mode 100644 index 00000000..34a3b197 Binary files /dev/null and b/data/fuzz/api/5aa45433beed76b16f367fe69a5f0f722e9785ff differ diff --git a/data/fuzz/api/5af6c73c6a666c0d483ed0c74f0469acbfb57de3 b/data/fuzz/api/5af6c73c6a666c0d483ed0c74f0469acbfb57de3 new file mode 100644 index 00000000..0e3debe5 --- /dev/null +++ b/data/fuzz/api/5af6c73c6a666c0d483ed0c74f0469acbfb57de3 @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/data/fuzz/api/5c069de850180f93424e0a1e042649c6df18c042 b/data/fuzz/api/5c069de850180f93424e0a1e042649c6df18c042 new file mode 100644 index 00000000..f6d1fdf0 Binary files /dev/null and b/data/fuzz/api/5c069de850180f93424e0a1e042649c6df18c042 differ diff --git a/data/fuzz/api/6035e522da2f771dbdaf1027b132429f0b41f0de b/data/fuzz/api/6035e522da2f771dbdaf1027b132429f0b41f0de new file mode 100644 index 00000000..7a11d7a6 --- /dev/null +++ b/data/fuzz/api/6035e522da2f771dbdaf1027b132429f0b41f0de @@ -0,0 +1 @@ +{{ \ No newline at end of file diff --git a/data/fuzz/api/61a850273fae4d59682e39e42beccbce43c7be10 b/data/fuzz/api/61a850273fae4d59682e39e42beccbce43c7be10 new file mode 100644 index 00000000..f3f92c06 Binary files /dev/null and b/data/fuzz/api/61a850273fae4d59682e39e42beccbce43c7be10 differ diff --git a/data/fuzz/api/6a3325de19b7a40f7f7abef68978d55137cc8991 b/data/fuzz/api/6a3325de19b7a40f7f7abef68978d55137cc8991 new file mode 100644 index 00000000..6ff85345 --- /dev/null +++ b/data/fuzz/api/6a3325de19b7a40f7f7abef68978d55137cc8991 @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/data/fuzz/api/6b815d2aed0dfa75ccad50351b21e9de5c91b350 b/data/fuzz/api/6b815d2aed0dfa75ccad50351b21e9de5c91b350 new file mode 100644 index 00000000..d3ab86da Binary files /dev/null and b/data/fuzz/api/6b815d2aed0dfa75ccad50351b21e9de5c91b350 differ diff --git a/data/fuzz/api/6d01dff409415c1f1299af3502f125eef9b71df5 b/data/fuzz/api/6d01dff409415c1f1299af3502f125eef9b71df5 new file mode 100644 index 00000000..473e34b4 --- /dev/null +++ b/data/fuzz/api/6d01dff409415c1f1299af3502f125eef9b71df5 @@ -0,0 +1 @@ +۱x#0! \ No newline at end of file diff --git a/data/fuzz/api/7075a8870ae3f1494ad3fbab6efd8590e6257a11 b/data/fuzz/api/7075a8870ae3f1494ad3fbab6efd8590e6257a11 new file mode 100644 index 00000000..dccd846e Binary files /dev/null and b/data/fuzz/api/7075a8870ae3f1494ad3fbab6efd8590e6257a11 differ diff --git a/data/fuzz/api/76d14ea91168626a28ed808ab86d116969ee5579 b/data/fuzz/api/76d14ea91168626a28ed808ab86d116969ee5579 new file mode 100644 index 00000000..c261d789 --- /dev/null +++ b/data/fuzz/api/76d14ea91168626a28ed808ab86d116969ee5579 @@ -0,0 +1 @@ +HeH"""H" \ No newline at end of file diff --git a/data/fuzz/api/7b8b2cd9ae0808e9066f31355ffad7f3a1590676 b/data/fuzz/api/7b8b2cd9ae0808e9066f31355ffad7f3a1590676 new file mode 100644 index 00000000..aaa91dd0 Binary files /dev/null and b/data/fuzz/api/7b8b2cd9ae0808e9066f31355ffad7f3a1590676 differ diff --git a/data/fuzz/api/81729bb73314953b2e73d8b6019fa666c24ed85d b/data/fuzz/api/81729bb73314953b2e73d8b6019fa666c24ed85d new file mode 100644 index 00000000..4e097b0d Binary files /dev/null and b/data/fuzz/api/81729bb73314953b2e73d8b6019fa666c24ed85d differ diff --git a/data/fuzz/api/818a038b030208a3fc21ef5f56006a6ca8612b29 b/data/fuzz/api/818a038b030208a3fc21ef5f56006a6ca8612b29 new file mode 100644 index 00000000..e867cf61 --- /dev/null +++ b/data/fuzz/api/818a038b030208a3fc21ef5f56006a6ca8612b29 @@ -0,0 +1 @@ +yZ?,?,?JZyZ?,?/,?JZ!ڞ%%JZ \ No newline at end of file diff --git a/data/fuzz/api/88fa28c86e37004eff702379366a79790a264c26 b/data/fuzz/api/88fa28c86e37004eff702379366a79790a264c26 new file mode 100644 index 00000000..86545ada --- /dev/null +++ b/data/fuzz/api/88fa28c86e37004eff702379366a79790a264c26 @@ -0,0 +1 @@ +GGGGGGGGGGGGGGG \ No newline at end of file diff --git a/data/fuzz/api/8c457b391748ce869e3ca1d14680a29a9a1dec7d b/data/fuzz/api/8c457b391748ce869e3ca1d14680a29a9a1dec7d new file mode 100644 index 00000000..7adae96c Binary files /dev/null and b/data/fuzz/api/8c457b391748ce869e3ca1d14680a29a9a1dec7d differ diff --git a/data/fuzz/api/92919e169d8a41b411c028c446ae64ef5e81d5be b/data/fuzz/api/92919e169d8a41b411c028c446ae64ef5e81d5be new file mode 100644 index 00000000..8e35457d Binary files /dev/null and b/data/fuzz/api/92919e169d8a41b411c028c446ae64ef5e81d5be differ diff --git a/data/fuzz/api/98f7620e6543aa94f208c6e97c3815c7fd86f570 b/data/fuzz/api/98f7620e6543aa94f208c6e97c3815c7fd86f570 new file mode 100644 index 00000000..98857b3a Binary files /dev/null and b/data/fuzz/api/98f7620e6543aa94f208c6e97c3815c7fd86f570 differ diff --git a/data/fuzz/api/9b4eb939e2fba6ce08c6bd6d9e2fb27e72ef9f4b b/data/fuzz/api/9b4eb939e2fba6ce08c6bd6d9e2fb27e72ef9f4b new file mode 100644 index 00000000..bb46b766 --- /dev/null +++ b/data/fuzz/api/9b4eb939e2fba6ce08c6bd6d9e2fb27e72ef9f4b @@ -0,0 +1 @@ +| \ No newline at end of file diff --git a/data/fuzz/api/a771e987294426159899c3c4caa2649d2762581a b/data/fuzz/api/a771e987294426159899c3c4caa2649d2762581a new file mode 100644 index 00000000..fed94f9b Binary files /dev/null and b/data/fuzz/api/a771e987294426159899c3c4caa2649d2762581a differ diff --git a/data/fuzz/api/abede5d1dff1fc2b18a7407755cd83536f82dca8 b/data/fuzz/api/abede5d1dff1fc2b18a7407755cd83536f82dca8 new file mode 100644 index 00000000..ea485f9b Binary files /dev/null and b/data/fuzz/api/abede5d1dff1fc2b18a7407755cd83536f82dca8 differ diff --git a/data/fuzz/api/b7b4e06ffc64fbb2c8974fd39541a1ef1b7493b4 b/data/fuzz/api/b7b4e06ffc64fbb2c8974fd39541a1ef1b7493b4 new file mode 100644 index 00000000..5158c06c --- /dev/null +++ b/data/fuzz/api/b7b4e06ffc64fbb2c8974fd39541a1ef1b7493b4 @@ -0,0 +1 @@ +!۲#!!8K!@.-!!#۔h!@s2!!k!@s! \ No newline at end of file diff --git a/data/fuzz/api/b8a185e28a598a75271c5060e2c2dd2084259d40 b/data/fuzz/api/b8a185e28a598a75271c5060e2c2dd2084259d40 new file mode 100644 index 00000000..583ae0ce Binary files /dev/null and b/data/fuzz/api/b8a185e28a598a75271c5060e2c2dd2084259d40 differ diff --git a/data/fuzz/api/ba6e31c9e02c1bf15255adcbfecb1ed9fb6c0604 b/data/fuzz/api/ba6e31c9e02c1bf15255adcbfecb1ed9fb6c0604 new file mode 100644 index 00000000..1467164b Binary files /dev/null and b/data/fuzz/api/ba6e31c9e02c1bf15255adcbfecb1ed9fb6c0604 differ diff --git a/data/fuzz/api/be424a3a8ef7d7cfb302f20a91d257ecdd6d39a3 b/data/fuzz/api/be424a3a8ef7d7cfb302f20a91d257ecdd6d39a3 new file mode 100644 index 00000000..2d3b02d8 Binary files /dev/null and b/data/fuzz/api/be424a3a8ef7d7cfb302f20a91d257ecdd6d39a3 differ diff --git a/data/fuzz/api/bf0394c4f2d4da81d778408bdeb3c325d5df0878 b/data/fuzz/api/bf0394c4f2d4da81d778408bdeb3c325d5df0878 new file mode 100644 index 00000000..2f63c255 Binary files /dev/null and b/data/fuzz/api/bf0394c4f2d4da81d778408bdeb3c325d5df0878 differ diff --git a/data/fuzz/api/bf4d13c7ee2607d3cefa3671e90ae41914a3817e b/data/fuzz/api/bf4d13c7ee2607d3cefa3671e90ae41914a3817e new file mode 100644 index 00000000..106b33d2 --- /dev/null +++ b/data/fuzz/api/bf4d13c7ee2607d3cefa3671e90ae41914a3817e @@ -0,0 +1 @@ +-۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱ \ No newline at end of file diff --git a/data/fuzz/api/c4addb5be9525b184e73f17ee52ad8f8f8009501 b/data/fuzz/api/c4addb5be9525b184e73f17ee52ad8f8f8009501 new file mode 100644 index 00000000..cdb479a4 Binary files /dev/null and b/data/fuzz/api/c4addb5be9525b184e73f17ee52ad8f8f8009501 differ diff --git a/data/fuzz/api/c85488a14efb3cd93838a2136d398912793fc8b5 b/data/fuzz/api/c85488a14efb3cd93838a2136d398912793fc8b5 new file mode 100644 index 00000000..55c89f6d --- /dev/null +++ b/data/fuzz/api/c85488a14efb3cd93838a2136d398912793fc8b5 @@ -0,0 +1 @@ +-%z,Z%?-[%/ \ No newline at end of file diff --git a/data/fuzz/api/c9c9699668662ac0d4c21cafd4b2e86f4b3db945 b/data/fuzz/api/c9c9699668662ac0d4c21cafd4b2e86f4b3db945 new file mode 100644 index 00000000..9b5ef660 --- /dev/null +++ b/data/fuzz/api/c9c9699668662ac0d4c21cafd4b2e86f4b3db945 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/fuzz/api/ca9afb0a362173525e7d4e8f87035bd1754ea13e b/data/fuzz/api/ca9afb0a362173525e7d4e8f87035bd1754ea13e new file mode 100644 index 00000000..cfa46426 Binary files /dev/null and b/data/fuzz/api/ca9afb0a362173525e7d4e8f87035bd1754ea13e differ diff --git a/data/fuzz/api/caee88cd2cc239b54cd1511c6fdd946362b01bd0 b/data/fuzz/api/caee88cd2cc239b54cd1511c6fdd946362b01bd0 new file mode 100644 index 00000000..793624e1 Binary files /dev/null and b/data/fuzz/api/caee88cd2cc239b54cd1511c6fdd946362b01bd0 differ diff --git a/data/fuzz/api/cb17bd1fc11b47f644bee4eb87716c30813ec66a b/data/fuzz/api/cb17bd1fc11b47f644bee4eb87716c30813ec66a new file mode 100644 index 00000000..37f52fd4 Binary files /dev/null and b/data/fuzz/api/cb17bd1fc11b47f644bee4eb87716c30813ec66a differ diff --git a/data/fuzz/api/cd0bff0bac8e5587fe5835e6f6b8831671bb8cb0 b/data/fuzz/api/cd0bff0bac8e5587fe5835e6f6b8831671bb8cb0 new file mode 100644 index 00000000..15790a41 Binary files /dev/null and b/data/fuzz/api/cd0bff0bac8e5587fe5835e6f6b8831671bb8cb0 differ diff --git a/data/fuzz/api/cdbebb288783b7afbfea18f41d74282a5a760baa b/data/fuzz/api/cdbebb288783b7afbfea18f41d74282a5a760baa new file mode 100644 index 00000000..1f5bbc70 --- /dev/null +++ b/data/fuzz/api/cdbebb288783b7afbfea18f41d74282a5a760baa @@ -0,0 +1 @@ +:GGGGGG$GGGGGGGGGGGGGGGGGR \ No newline at end of file diff --git a/data/fuzz/api/ce547c1571410a433155945e3c8efd6608a8cd55 b/data/fuzz/api/ce547c1571410a433155945e3c8efd6608a8cd55 new file mode 100644 index 00000000..ee419682 --- /dev/null +++ b/data/fuzz/api/ce547c1571410a433155945e3c8efd6608a8cd55 @@ -0,0 +1 @@ +f!fffffff!ff! \ No newline at end of file diff --git a/data/fuzz/api/d05dc6ccf0263f230068dcff5cfa76e18b400493 b/data/fuzz/api/d05dc6ccf0263f230068dcff5cfa76e18b400493 new file mode 100644 index 00000000..d8486476 Binary files /dev/null and b/data/fuzz/api/d05dc6ccf0263f230068dcff5cfa76e18b400493 differ diff --git a/data/fuzz/api/d09396127da6714e99f1f18d881939e19749c627 b/data/fuzz/api/d09396127da6714e99f1f18d881939e19749c627 new file mode 100644 index 00000000..5ef8e816 --- /dev/null +++ b/data/fuzz/api/d09396127da6714e99f1f18d881939e19749c627 @@ -0,0 +1 @@ +YC \ No newline at end of file diff --git a/data/fuzz/api/d2cd83b299a8b86293335172c5379b825b07c9fc b/data/fuzz/api/d2cd83b299a8b86293335172c5379b825b07c9fc new file mode 100644 index 00000000..4b2c15d1 Binary files /dev/null and b/data/fuzz/api/d2cd83b299a8b86293335172c5379b825b07c9fc differ diff --git a/data/fuzz/api/d62f1806de56aed27a3782f7c390ba2937b34a34 b/data/fuzz/api/d62f1806de56aed27a3782f7c390ba2937b34a34 new file mode 100644 index 00000000..a324cd33 --- /dev/null +++ b/data/fuzz/api/d62f1806de56aed27a3782f7c390ba2937b34a34 @@ -0,0 +1 @@ +/ /00 00 0 z 00 0 0 000 00 0 0[ \ No newline at end of file diff --git a/data/fuzz/api/db7949e914d804216f190259bae93aff4315b148 b/data/fuzz/api/db7949e914d804216f190259bae93aff4315b148 new file mode 100644 index 00000000..c87b22fd Binary files /dev/null and b/data/fuzz/api/db7949e914d804216f190259bae93aff4315b148 differ diff --git a/data/fuzz/api/dcd80dfd1c3a2f7c81b943692a5529a34716c7bb b/data/fuzz/api/dcd80dfd1c3a2f7c81b943692a5529a34716c7bb new file mode 100644 index 00000000..9fb10ce9 Binary files /dev/null and b/data/fuzz/api/dcd80dfd1c3a2f7c81b943692a5529a34716c7bb differ diff --git a/data/fuzz/api/def5720e6378d275f66d35f2c3435ac975199339 b/data/fuzz/api/def5720e6378d275f66d35f2c3435ac975199339 new file mode 100644 index 00000000..46425b62 Binary files /dev/null and b/data/fuzz/api/def5720e6378d275f66d35f2c3435ac975199339 differ diff --git a/data/fuzz/api/df1149c3e7f609ec27ca8ab4e3042352aa1a028a b/data/fuzz/api/df1149c3e7f609ec27ca8ab4e3042352aa1a028a new file mode 100644 index 00000000..cf121d4f Binary files /dev/null and b/data/fuzz/api/df1149c3e7f609ec27ca8ab4e3042352aa1a028a differ diff --git a/data/fuzz/api/df77f4f8c395eea8b3eb53549fb9a51e4329eb68 b/data/fuzz/api/df77f4f8c395eea8b3eb53549fb9a51e4329eb68 new file mode 100644 index 00000000..8f694afd Binary files /dev/null and b/data/fuzz/api/df77f4f8c395eea8b3eb53549fb9a51e4329eb68 differ diff --git a/data/fuzz/api/e0eb473d04e391632367c82eae68e3fc03b27fd6 b/data/fuzz/api/e0eb473d04e391632367c82eae68e3fc03b27fd6 new file mode 100644 index 00000000..3febbc74 Binary files /dev/null and b/data/fuzz/api/e0eb473d04e391632367c82eae68e3fc03b27fd6 differ diff --git a/data/fuzz/api/e5486aa467c80e54494e23136b3751aa188d8acc b/data/fuzz/api/e5486aa467c80e54494e23136b3751aa188d8acc new file mode 100644 index 00000000..48be9a0d Binary files /dev/null and b/data/fuzz/api/e5486aa467c80e54494e23136b3751aa188d8acc differ diff --git a/data/fuzz/api/e573c1e57431b2d466c8c41408eb764892321bdd b/data/fuzz/api/e573c1e57431b2d466c8c41408eb764892321bdd new file mode 100644 index 00000000..15d06d07 --- /dev/null +++ b/data/fuzz/api/e573c1e57431b2d466c8c41408eb764892321bdd @@ -0,0 +1 @@ +{{[///%/{/////:{////[//{/////:{////[///[//[/{///:{////[///[//[/{//[/////[/////[/![ \ No newline at end of file diff --git a/data/fuzz/api/e63a119492d56193db5b359a99819f46cfd3164f b/data/fuzz/api/e63a119492d56193db5b359a99819f46cfd3164f new file mode 100644 index 00000000..e62b5730 Binary files /dev/null and b/data/fuzz/api/e63a119492d56193db5b359a99819f46cfd3164f differ diff --git a/data/fuzz/api/ea2e49089fabc8c14e8585611ed5591b0667482c b/data/fuzz/api/ea2e49089fabc8c14e8585611ed5591b0667482c new file mode 100644 index 00000000..78c51382 Binary files /dev/null and b/data/fuzz/api/ea2e49089fabc8c14e8585611ed5591b0667482c differ diff --git a/data/fuzz/api/ea602310ae83e245a51971758a86e97f203d6095 b/data/fuzz/api/ea602310ae83e245a51971758a86e97f203d6095 new file mode 100644 index 00000000..9baa2439 --- /dev/null +++ b/data/fuzz/api/ea602310ae83e245a51971758a86e97f203d6095 @@ -0,0 +1 @@ +5 \ No newline at end of file diff --git a/data/fuzz/api/eb487de4e5b68bc61fa54da5140481e7a07f11c8 b/data/fuzz/api/eb487de4e5b68bc61fa54da5140481e7a07f11c8 new file mode 100644 index 00000000..533e261c --- /dev/null +++ b/data/fuzz/api/eb487de4e5b68bc61fa54da5140481e7a07f11c8 @@ -0,0 +1 @@ +UUUUUUX}aMa[ \ No newline at end of file diff --git a/data/fuzz/api/ee1db5311bc9c881e92f8d78316f06e491190e0f b/data/fuzz/api/ee1db5311bc9c881e92f8d78316f06e491190e0f new file mode 100644 index 00000000..ce6443f8 Binary files /dev/null and b/data/fuzz/api/ee1db5311bc9c881e92f8d78316f06e491190e0f differ diff --git a/data/fuzz/api/ee292cb81ed36de2ac8c3c4e07f3e61f881ea9e7 b/data/fuzz/api/ee292cb81ed36de2ac8c3c4e07f3e61f881ea9e7 new file mode 100644 index 00000000..f504c23d --- /dev/null +++ b/data/fuzz/api/ee292cb81ed36de2ac8c3c4e07f3e61f881ea9e7 @@ -0,0 +1 @@ +/@ \ No newline at end of file diff --git a/data/fuzz/api/f121c7c81b8a962b8d31c9c8710c0371cf0a6452 b/data/fuzz/api/f121c7c81b8a962b8d31c9c8710c0371cf0a6452 new file mode 100644 index 00000000..b2c9b3c8 Binary files /dev/null and b/data/fuzz/api/f121c7c81b8a962b8d31c9c8710c0371cf0a6452 differ diff --git a/data/fuzz/api/f3a446dc2603faa944de1cf8a73a575cc6ae8c80 b/data/fuzz/api/f3a446dc2603faa944de1cf8a73a575cc6ae8c80 new file mode 100644 index 00000000..eca56f55 Binary files /dev/null and b/data/fuzz/api/f3a446dc2603faa944de1cf8a73a575cc6ae8c80 differ diff --git a/data/fuzz/api/f463d1512a21608f3dd67f3ad5b3f1bb19bd0d69 b/data/fuzz/api/f463d1512a21608f3dd67f3ad5b3f1bb19bd0d69 new file mode 100644 index 00000000..0124bcc6 Binary files /dev/null and b/data/fuzz/api/f463d1512a21608f3dd67f3ad5b3f1bb19bd0d69 differ diff --git a/data/fuzz/api/f5131c62ec188dbc950017f19dace4fcc50532d6 b/data/fuzz/api/f5131c62ec188dbc950017f19dace4fcc50532d6 new file mode 100644 index 00000000..77883991 Binary files /dev/null and b/data/fuzz/api/f5131c62ec188dbc950017f19dace4fcc50532d6 differ diff --git a/data/fuzz/api/f6a93cdf876262ecb6428a420c3e19319b941fb4 b/data/fuzz/api/f6a93cdf876262ecb6428a420c3e19319b941fb4 new file mode 100644 index 00000000..ea80337f Binary files /dev/null and b/data/fuzz/api/f6a93cdf876262ecb6428a420c3e19319b941fb4 differ diff --git a/data/fuzz/api/f770e04dfd1dbc59d54a991c908eb72fd349a6be b/data/fuzz/api/f770e04dfd1dbc59d54a991c908eb72fd349a6be new file mode 100644 index 00000000..1f6f91b6 Binary files /dev/null and b/data/fuzz/api/f770e04dfd1dbc59d54a991c908eb72fd349a6be differ diff --git a/data/fuzz/api/f8ca2fde2281ab28f40091fb8083e9eef068e01c b/data/fuzz/api/f8ca2fde2281ab28f40091fb8083e9eef068e01c new file mode 100644 index 00000000..659ef86a --- /dev/null +++ b/data/fuzz/api/f8ca2fde2281ab28f40091fb8083e9eef068e01c @@ -0,0 +1 @@ +H,![ \ No newline at end of file diff --git a/data/fuzz/api/f8fd548dba02bfd14d0b116065a89b02452c739e b/data/fuzz/api/f8fd548dba02bfd14d0b116065a89b02452c739e new file mode 100644 index 00000000..bfbff0c5 Binary files /dev/null and b/data/fuzz/api/f8fd548dba02bfd14d0b116065a89b02452c739e differ diff --git a/data/fuzz/api/f98cdedc6fbf42753dc16e4d5c8de1eb5b26218f b/data/fuzz/api/f98cdedc6fbf42753dc16e4d5c8de1eb5b26218f new file mode 100644 index 00000000..eacab4d9 Binary files /dev/null and b/data/fuzz/api/f98cdedc6fbf42753dc16e4d5c8de1eb5b26218f differ diff --git a/data/fuzz/api/fd04f77309b4c46d1eda62ecf8dc7d51ec95bb36 b/data/fuzz/api/fd04f77309b4c46d1eda62ecf8dc7d51ec95bb36 new file mode 100644 index 00000000..5a1aaaf3 Binary files /dev/null and b/data/fuzz/api/fd04f77309b4c46d1eda62ecf8dc7d51ec95bb36 differ diff --git a/data/fuzz/api/fd62c7b111a1e1e3f526f7e91407d043af7abfa9 b/data/fuzz/api/fd62c7b111a1e1e3f526f7e91407d043af7abfa9 new file mode 100644 index 00000000..98b5cded Binary files /dev/null and b/data/fuzz/api/fd62c7b111a1e1e3f526f7e91407d043af7abfa9 differ diff --git a/data/fuzz/api/fd835958194041f325074a5e1bbd063f44c75075 b/data/fuzz/api/fd835958194041f325074a5e1bbd063f44c75075 new file mode 100644 index 00000000..6007ea1c Binary files /dev/null and b/data/fuzz/api/fd835958194041f325074a5e1bbd063f44c75075 differ diff --git a/data/fuzz/api/fdeeb02ad36e89b018b71dea53e5faaee2081ca0 b/data/fuzz/api/fdeeb02ad36e89b018b71dea53e5faaee2081ca0 new file mode 100644 index 00000000..e836738f --- /dev/null +++ b/data/fuzz/api/fdeeb02ad36e89b018b71dea53e5faaee2081ca0 @@ -0,0 +1 @@ +K# \ No newline at end of file diff --git a/doc/allocated_memory.gnuplot b/doc/allocated_memory.gnuplot new file mode 100755 index 00000000..de16ff91 --- /dev/null +++ b/doc/allocated_memory.gnuplot @@ -0,0 +1,44 @@ +#!/usr/bin/gnuplot + +#set terminal pngcairo +#set terminal pngcairo size 730,510 enhanced font 'Verdana,10' +set terminal pngcairo size 800,600 enhanced font 'Verdana,10' + +# define axis +# remove border on top and right and set color to gray +set style line 11 lc rgb '#808080' lt 1 +set border 3 back ls 11 +set tics nomirror +# define grid +set style line 12 lc rgb '#808080' lt 0 lw 1 +set grid back ls 12 + +# line styles +set style line 1 lt 1 lc rgb '#1B9E77' # dark teal +set style line 2 lt 1 lc rgb '#D95F02' # dark orange +set style line 3 lt 1 lc rgb '#7570B3' # dark lilac +set style line 4 lt 1 lc rgb '#E7298A' # dark magenta +set style line 5 lt 1 lc rgb '#66A61E' # dark lime green +set style line 6 lt 1 lc rgb '#E6AB02' # dark banana +set style line 7 lt 1 lc rgb '#A6761D' # dark tan +set style line 8 lt 1 lc rgb '#666666' # dark gray + + +set style line 101 lc rgb '#808080' lt 1 lw 1 +set border 3 front ls 101 +set tics nomirror out scale 0.75 + +set key left top + +set output 'allocated_memory.png' + +set xlabel "Runtime [s]" +set ylabel "Allocated memory [MB]" + +set title "Inserting 10 Million uint64\\\_t -> uint64\\\_t pairs" + +# allocated_memory_segmented_vector.txt allocated_memory_std_unordered_map.txt allocated_memory_std_vector.txt +plot \ + 'allocated_memory_segmented_vector.txt' using ($1):($2/1e6) w steps ls 1 lw 2 title "ankerl::unordered\\\_dense::segmented\\\_map" , \ + 'allocated_memory_std_vector.txt' using ($1):($2/1e6) w steps ls 2 lw 2 title "ankerl::unordered\\\_dense::map" , \ + 'allocated_memory_absl_flat_hash_map.txt' using ($1):($2/1e6) w steps ls 3 lw 2 title "absl::flat\\\_hash\\\_map" diff --git a/doc/allocated_memory.png b/doc/allocated_memory.png new file mode 100644 index 00000000..29d09a9e Binary files /dev/null and b/doc/allocated_memory.png differ diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h index e294bdb4..dc4de8ab 100644 --- a/include/ankerl/unordered_dense.h +++ b/include/ankerl/unordered_dense.h @@ -1,7 +1,7 @@ ///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// // A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. -// Version 3.1.1 +// Version 4.0.0 // https://github.com/martinus/unordered_dense // // Licensed under the MIT License . @@ -30,12 +30,15 @@ #define ANKERL_UNORDERED_DENSE_H // see https://semver.org/spec/v2.0.0.html -#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 3 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes -#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 1 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality -#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 1 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes +#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 4 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes +#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality +#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes // API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ANKERL_UNORDERED_DENSE_VERSION_CONCAT(major, minor, patch) ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) #define ANKERL_UNORDERED_DENSE_NAMESPACE \ ANKERL_UNORDERED_DENSE_VERSION_CONCAT( \ @@ -57,9 +60,9 @@ // exceptions #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) -# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 1 +# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 1 // NOLINT(cppcoreguidelines-macro-usage) #else -# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 0 +# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 0 // NOLINT(cppcoreguidelines-macro-usage) #endif #ifdef _MSC_VER # define ANKERL_UNORDERED_DENSE_NOINLINE __declspec(noinline) @@ -89,20 +92,13 @@ # include // for abort # endif -# define ANKERL_UNORDERED_DENSE_PMR 0 // NOLINT(cppcoreguidelines-macro-usage) # if defined(__has_include) # if __has_include() -# undef ANKERL_UNORDERED_DENSE_PMR -# define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage) -# define ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR \ - std::pmr::polymorphic_allocator // NOLINT(cppcoreguidelines-macro-usage) -# include // for polymorphic_allocator +# define ANKERL_UNORDERED_DENSE_PMR std::pmr // NOLINT(cppcoreguidelines-macro-usage) +# include // for polymorphic_allocator # elif __has_include() -# undef ANKERL_UNORDERED_DENSE_PMR -# define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage) -# define ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR \ - std::experimental::pmr::polymorphic_allocator // NOLINT(cppcoreguidelines-macro-usage) -# include // for polymorphic_allocator +# define ANKERL_UNORDERED_DENSE_PMR std::experimental::pmr // NOLINT(cppcoreguidelines-macro-usage) +# include // for polymorphic_allocator # endif # endif @@ -428,7 +424,7 @@ constexpr bool is_map_v = !std::is_void_v; // clang-format off template -constexpr bool is_transparent_v = is_detected_v&& is_detected_v; +constexpr bool is_transparent_v = is_detected_v && is_detected_v; // clang-format on template @@ -446,19 +442,320 @@ struct base_table_type_map { // base type for set doesn't have mapped_type struct base_table_type_set {}; +} // namespace detail + +// Very much like std::deque, but faster for indexing (in most cases). As of now this doesn't implement the full std::vector +// API, but merely what's necessary to work as an underlying container for ankerl::unordered_dense::{map, set}. +// It allocates blocks of equal size and puts them into the m_blocks vector. That means it can grow simply by adding a new +// block to the back of m_blocks, and doesn't double its size like an std::vector. The disadvantage is that memory is not +// linear and thus there is one more indirection necessary for indexing. +template , size_t MaxSegmentSizeBytes = 4096> +class segmented_vector { + template + class iter_t; + +public: + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using difference_type = typename std::allocator_traits::difference_type; + using value_type = T; + using size_type = std::size_t; + using reference = T&; + using const_reference = T const&; + using iterator = iter_t; + using const_iterator = iter_t; + +private: + using vec_alloc = typename std::allocator_traits::template rebind_alloc; + std::vector m_blocks{}; + size_t m_size{}; + + // Calculates the maximum number for x in (s << x) <= max_val + static constexpr auto num_bits_closest(size_t max_val, size_t s) -> size_t { + auto f = size_t{0}; + while (s << (f + 1) <= max_val) { + ++f; + } + return f; + } + + using self_t = segmented_vector; + static constexpr auto num_bits = num_bits_closest(MaxSegmentSizeBytes, sizeof(T)); + static constexpr auto num_elements_in_block = 1U << num_bits; + static constexpr auto mask = num_elements_in_block - 1U; + + /** + * Iterator class doubles as const_iterator and iterator + */ + template + class iter_t { + using ptr_t = typename std::conditional_t; + ptr_t m_data{}; + size_t m_idx{}; + + template + friend class iter_t; + + public: + using difference_type = segmented_vector::difference_type; + using value_type = T; + using reference = typename std::conditional_t; + using pointer = typename std::conditional_t; + using iterator_category = std::forward_iterator_tag; + + iter_t() noexcept = default; + + template ::type> + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) + constexpr iter_t(iter_t const& other) noexcept + : m_data(other.m_data) + , m_idx(other.m_idx) {} + + constexpr iter_t(ptr_t data, size_t idx) noexcept + : m_data(data) + , m_idx(idx) {} + + template ::type> + constexpr auto operator=(iter_t const& other) noexcept -> iter_t& { + m_data = other.m_data; + m_idx = other.m_idx; + return *this; + } + + constexpr auto operator++() noexcept -> iter_t& { + ++m_idx; + return *this; + } + + constexpr auto operator+(difference_type diff) noexcept -> iter_t { + return {m_data, static_cast(static_cast(m_idx) + diff)}; + } + + template + constexpr auto operator-(iter_t const& other) noexcept -> difference_type { + return static_cast(m_idx) - static_cast(other.m_idx); + } + + constexpr auto operator*() const noexcept -> reference { + return m_data[m_idx >> num_bits][m_idx & mask]; + } + + constexpr auto operator->() const noexcept -> pointer { + return &m_data[m_idx >> num_bits][m_idx & mask]; + } + + template + constexpr auto operator==(iter_t const& o) const noexcept -> bool { + return m_idx == o.m_idx; + } + + template + constexpr auto operator!=(iter_t const& o) const noexcept -> bool { + return !(*this == o); + } + }; + + // slow path: need to allocate a new segment every once in a while + void increase_capacity() { + auto ba = Allocator(m_blocks.get_allocator()); + pointer block = std::allocator_traits::allocate(ba, num_elements_in_block); + m_blocks.push_back(block); + } + + // Moves everything from other + void append_everything_from(segmented_vector&& other) { + reserve(size() + other.size()); + for (auto&& o : other) { + emplace_back(std::move(o)); + } + } + + // Copies everything from other + void append_everything_from(segmented_vector const& other) { + reserve(size() + other.size()); + for (auto const& o : other) { + emplace_back(o); + } + } + + void dealloc() { + auto ba = Allocator(m_blocks.get_allocator()); + for (auto ptr : m_blocks) { + std::allocator_traits::deallocate(ba, ptr, num_elements_in_block); + } + } + + [[nodiscard]] static constexpr auto calc_num_blocks_for_capacity(size_t capacity) { + return (capacity + num_elements_in_block - 1U) / num_elements_in_block; + } + +public: + segmented_vector() = default; + + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) + segmented_vector(Allocator alloc) + : m_blocks(vec_alloc(alloc)) {} + + segmented_vector(segmented_vector&& other, Allocator alloc) + : m_blocks(vec_alloc(alloc)) { + if (other.get_allocator() == alloc) { + *this = std::move(other); + } else { + // Oh my, allocator is different so we need to copy everything. + append_everything_from(std::move(other)); + } + } + + segmented_vector(segmented_vector&& other) noexcept + : m_blocks(std::move(other.m_blocks)) + , m_size(std::exchange(other.m_size, {})) {} + + segmented_vector(segmented_vector const& other, Allocator alloc) + : m_blocks(vec_alloc(alloc)) { + append_everything_from(other); + } + + segmented_vector(segmented_vector const& other) { + append_everything_from(other); + } + + auto operator=(segmented_vector const& other) -> segmented_vector& { + if (this == &other) { + return *this; + } + clear(); + append_everything_from(other); + return *this; + } + + auto operator=(segmented_vector&& other) noexcept -> segmented_vector& { + clear(); + dealloc(); + m_blocks = std::move(other.m_blocks); + m_size = std::exchange(other.m_size, {}); + return *this; + } + + ~segmented_vector() { + clear(); + dealloc(); + } + + [[nodiscard]] constexpr auto size() const -> size_t { + return m_size; + } + + [[nodiscard]] constexpr auto capacity() const -> size_t { + return m_blocks.size() * num_elements_in_block; + } + + // Indexing is highly performance critical + [[nodiscard]] constexpr auto operator[](size_t i) const noexcept -> T const& { + return m_blocks[i >> num_bits][i & mask]; + } + + [[nodiscard]] constexpr auto operator[](size_t i) noexcept -> T& { + return m_blocks[i >> num_bits][i & mask]; + } + + [[nodiscard]] constexpr auto begin() -> iterator { + return {m_blocks.data(), 0U}; + } + [[nodiscard]] constexpr auto begin() const -> const_iterator { + return {m_blocks.data(), 0U}; + } + [[nodiscard]] constexpr auto cbegin() const -> const_iterator { + return {m_blocks.data(), 0U}; + } + + [[nodiscard]] constexpr auto end() -> iterator { + return {m_blocks.data(), m_size}; + } + [[nodiscard]] constexpr auto end() const -> const_iterator { + return {m_blocks.data(), m_size}; + } + [[nodiscard]] constexpr auto cend() const -> const_iterator { + return {m_blocks.data(), m_size}; + } + + [[nodiscard]] constexpr auto back() -> reference { + return operator[](m_size - 1); + } + [[nodiscard]] constexpr auto back() const -> const_reference { + return operator[](m_size - 1); + } + + void pop_back() { + back().~T(); + --m_size; + } + + [[nodiscard]] auto empty() const { + return 0 == m_size; + } + + void reserve(size_t new_capacity) { + m_blocks.reserve(calc_num_blocks_for_capacity(new_capacity)); + while (new_capacity > capacity()) { + increase_capacity(); + } + } + + [[nodiscard]] auto get_allocator() const -> allocator_type { + return allocator_type{m_blocks.get_allocator()}; + } + + template + auto emplace_back(Args&&... args) -> reference { + if (m_size == capacity()) { + increase_capacity(); + } + auto* ptr = static_cast(&operator[](m_size)); + auto& ref = *new (ptr) T(std::forward(args)...); + ++m_size; + return ref; + } + + void clear() { + if constexpr (!std::is_trivially_destructible_v) { + for (size_t i = 0, s = size(); i < s; ++i) { + operator[](i).~T(); + } + } + m_size = 0; + } + + void shrink_to_fit() { + auto ba = Allocator(m_blocks.get_allocator()); + auto num_blocks_required = calc_num_blocks_for_capacity(m_size); + while (m_blocks.size() > num_blocks_required) { + std::allocator_traits::deallocate(ba, m_blocks.back(), num_elements_in_block); + m_blocks.pop_back(); + } + m_blocks.shrink_to_fit(); + } +}; + +namespace detail { + // This is it, the table. Doubles as map and set, and uses `void` for T when its used as a set. template + class Bucket, + bool IsSegmented> class table : public std::conditional_t, base_table_type_map, base_table_type_set> { + using underlying_value_type = typename std::conditional_t, std::pair, Key>; + using underlying_container_type = std::conditional_t, + std::vector>; + public: - using value_container_type = std::conditional_t< - is_detected_v, - AllocatorOrContainer, - typename std::vector, std::pair, Key>, AllocatorOrContainer>>; + using value_container_type = std:: + conditional_t, AllocatorOrContainer, underlying_container_type>; private: using bucket_alloc = @@ -492,7 +789,8 @@ class table : public std::conditional_t, base_table_type_map, bas static_assert(std::is_trivially_copyable_v, "assert we can just memset / memcpy"); value_container_type m_values{}; // Contains all the key-value pairs in one densely stored container. No holes. - typename std::allocator_traits::pointer m_buckets{}; + using bucket_pointer = typename std::allocator_traits::pointer; + bucket_pointer m_buckets{}; size_t m_num_buckets = 0; size_t m_max_bucket_capacity = 0; float m_max_load_factor = default_max_load_factor; @@ -507,8 +805,7 @@ class table : public std::conditional_t, base_table_type_map, bas } // Helper to access bucket through pointer types - [[nodiscard]] static constexpr auto at(typename std::allocator_traits::pointer bucket_ptr, size_t offset) - -> Bucket& { + [[nodiscard]] static constexpr auto at(bucket_pointer bucket_ptr, size_t offset) -> Bucket& { return *(bucket_ptr + static_cast::difference_type>(offset)); } @@ -1519,16 +1816,31 @@ template , class AllocatorOrContainer = std::allocator>, class Bucket = bucket_type::standard> -using map = detail::table; +using map = detail::table; + +template , + class KeyEqual = std::equal_to, + class AllocatorOrContainer = std::allocator>, + class Bucket = bucket_type::standard> +using segmented_map = detail::table; + +template , + class KeyEqual = std::equal_to, + class AllocatorOrContainer = std::allocator, + class Bucket = bucket_type::standard> +using set = detail::table; template , class KeyEqual = std::equal_to, class AllocatorOrContainer = std::allocator, class Bucket = bucket_type::standard> -using set = detail::table; +using segmented_set = detail::table; -# if ANKERL_UNORDERED_DENSE_PMR +# if defined(ANKERL_UNORDERED_DENSE_PMR) namespace pmr { @@ -1537,10 +1849,23 @@ template , class KeyEqual = std::equal_to, class Bucket = bucket_type::standard> -using map = detail::table>, Bucket>; +using map = + detail::table>, Bucket, false>; + +template , + class KeyEqual = std::equal_to, + class Bucket = bucket_type::standard> +using segmented_map = + detail::table>, Bucket, true>; + +template , class KeyEqual = std::equal_to, class Bucket = bucket_type::standard> +using set = detail::table, Bucket, false>; template , class KeyEqual = std::equal_to, class Bucket = bucket_type::standard> -using set = detail::table, Bucket>; +using segmented_set = + detail::table, Bucket, true>; } // namespace pmr @@ -1558,11 +1883,18 @@ using set = detail::table +template // NOLINTNEXTLINE(cert-dcl58-cpp) -auto erase_if(ankerl::unordered_dense::detail::table& map, Pred pred) - -> size_t { - using map_t = ankerl::unordered_dense::detail::table; +auto erase_if(ankerl::unordered_dense::detail::table& map, + Pred pred) -> size_t { + using map_t = ankerl::unordered_dense::detail::table; // going back to front because erase() invalidates the end iterator auto const old_size = map.size(); diff --git a/meson.build b/meson.build index e4607951..8b86d45a 100644 --- a/meson.build +++ b/meson.build @@ -18,9 +18,14 @@ # project('unordered_dense', 'cpp', - version: '3.1.1', + version: '4.0.0', license: 'MIT', - default_options : ['cpp_std=c++17', 'warning_level=3', 'werror=true']) + default_options : [ + 'cpp_std=c++17', + 'warning_level=3', + 'werror=true', + 'b_ndebug=true', # otherwise absl is really slow! + ]) incdir = include_directories('include') subdir('test') diff --git a/subprojects/abseil-cpp.wrap b/subprojects/abseil-cpp.wrap new file mode 100644 index 00000000..29ad8c2e --- /dev/null +++ b/subprojects/abseil-cpp.wrap @@ -0,0 +1,23 @@ +[wrap-file] +directory = abseil-cpp-20220623.0 +source_url = https://github.com/abseil/abseil-cpp/archive/20220623.0.tar.gz +source_filename = abseil-cpp-20220623.0.tar.gz +source_hash = 4208129b49006089ba1d6710845a45e31c59b0ab6bff9e5788a87f55c5abd602 +patch_filename = abseil-cpp_20220623.0-2_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/abseil-cpp_20220623.0-2/get_patch +patch_hash = d19cb16610d9310658a815ebcd87a9e2966aafbd57964341c0d1a3a3778c03b6 +wrapdb_version = 20220623.0-2 + +[provide] +absl_base = absl_base_dep +absl_container = absl_container_dep +absl_debugging = absl_debugging_dep +absl_flags = absl_flags_dep +absl_hash = absl_hash_dep +absl_numeric = absl_numeric_dep +absl_random = absl_random_dep +absl_status = absl_status_dep +absl_strings = absl_strings_dep +absl_synchronization = absl_synchronization_dep +absl_time = absl_time_dep +absl_types = absl_types_dep diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap index bcc6e046..0ea7eb38 100644 --- a/subprojects/fmt.wrap +++ b/subprojects/fmt.wrap @@ -1,12 +1,12 @@ [wrap-file] -directory = fmt-9.0.0 -source_url = https://github.com/fmtlib/fmt/archive/9.0.0.tar.gz -source_filename = fmt-9.0.0.tar.gz -source_hash = 9a1e0e9e843a356d65c7604e2c8bf9402b50fe294c355de0095ebd42fb9bd2c5 -patch_filename = fmt_9.0.0-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.0.0-1/get_patch -patch_hash = 5f12924065e0fe7ccae40593d256a082955c273cb2880b3e3de05df9d8d10697 -wrapdb_version = 9.0.0-1 +directory = fmt-9.1.0 +source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz +source_filename = fmt-9.1.0.tar.gz +source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2 +patch_filename = fmt_9.1.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-1/get_patch +patch_hash = 4557b9ba87b3eb63694ed9b21d1a2117d4a97ca56b91085b10288e9a5294adf8 +wrapdb_version = 9.1.0-1 [provide] fmt = fmt_dep diff --git a/test/app/counter.cpp b/test/app/counter.cpp index 481f5038..e763296b 100644 --- a/test/app/counter.cpp +++ b/test/app/counter.cpp @@ -36,7 +36,7 @@ counter::obj::obj(const size_t& data, counter& counts) std::abort(); } } - ++m_counts->m_ctor; + ++m_counts->m_data.m_ctor; } counter::obj::obj(const counter::obj& o) @@ -53,7 +53,7 @@ counter::obj::obj(const counter::obj& o) } } if (nullptr != m_counts) { - ++m_counts->m_copy_ctor; + ++m_counts->m_data.m_copy_ctor; } } @@ -71,7 +71,7 @@ counter::obj::obj(counter::obj&& o) noexcept } } if (nullptr != m_counts) { - ++m_counts->m_move_ctor; + ++m_counts->m_data.m_move_ctor; } } @@ -83,7 +83,7 @@ counter::obj::~obj() { } } if (nullptr != m_counts) { - ++m_counts->m_dtor; + ++m_counts->m_data.m_dtor; } else { ++static_dtor; } @@ -97,7 +97,7 @@ auto counter::obj::operator==(obj const& o) const -> bool { } } if (nullptr != m_counts) { - ++m_counts->m_equals; + ++m_counts->m_data.m_equals; } return m_data == o.m_data; } @@ -110,7 +110,7 @@ auto counter::obj::operator<(obj const& o) const -> bool { } } if (nullptr != m_counts) { - ++m_counts->m_less; + ++m_counts->m_data.m_less; } return m_data < o.m_data; } @@ -125,7 +125,7 @@ auto counter::obj::operator=(obj const& o) -> counter::obj& { } m_counts = o.m_counts; if (nullptr != m_counts) { - ++m_counts->m_assign; + ++m_counts->m_data.m_assign; } m_data = o.m_data; return *this; @@ -143,21 +143,21 @@ auto counter::obj::operator=(obj&& o) noexcept -> counter::obj& { } m_data = o.m_data; if (nullptr != m_counts) { - ++m_counts->m_move_assign; + ++m_counts->m_data.m_move_assign; } return *this; } auto counter::obj::get() const -> size_t const& { if (nullptr != m_counts) { - ++m_counts->m_const_get; + ++m_counts->m_data.m_const_get; } return m_data; } auto counter::obj::get() -> size_t& { if (nullptr != m_counts) { - ++m_counts->m_get; + ++m_counts->m_data.m_get; } return m_data; } @@ -173,13 +173,13 @@ void counter::obj::swap(obj& other) { swap(m_data, other.m_data); swap(m_counts, other.m_counts); if (nullptr != m_counts) { - ++m_counts->m_swaps; + ++m_counts->m_data.m_swaps; } } auto counter::obj::get_for_hash() const -> size_t { if (nullptr != m_counts) { - ++m_counts->m_hash; + ++m_counts->m_data.m_hash; } return m_data; } @@ -196,17 +196,19 @@ void counter::check_all_done() const { test::print("ERROR at ~counter(): got {} objects still alive!", singleton_constructed_objects().size()); std::abort(); } - if (m_dtor + static_dtor != m_ctor + static_default_ctor + m_copy_ctor + m_default_ctor + m_move_ctor) { + + if (m_data.m_dtor + static_dtor != + m_data.m_ctor + static_default_ctor + m_data.m_copy_ctor + m_data.m_default_ctor + m_data.m_move_ctor) { test::print("ERROR at ~counter(): number of counts does not match!\n"); test::print( "{} dtor + {} staticDtor != {} ctor + {} staticDefaultCtor + {} copyCtor + {} defaultCtor + {} moveCtor\n", - m_dtor, + m_data.m_dtor, static_dtor, - m_ctor, + m_data.m_ctor, static_default_ctor, - m_copy_ctor, - m_default_ctor, - m_move_ctor); + m_data.m_copy_ctor, + m_data.m_default_ctor, + m_data.m_move_ctor); std::abort(); } } @@ -217,25 +219,26 @@ counter::~counter() { } auto counter::total() const -> size_t { - return m_ctor + static_default_ctor + m_copy_ctor + (m_dtor + static_dtor) + m_equals + m_less + m_assign + m_swaps + - m_get + m_const_get + m_hash + m_move_ctor + m_move_assign; + return m_data.m_ctor + static_default_ctor + m_data.m_copy_ctor + (m_data.m_dtor + static_dtor) + m_data.m_equals + + m_data.m_less + m_data.m_assign + m_data.m_swaps + m_data.m_get + m_data.m_const_get + m_data.m_hash + + m_data.m_move_ctor + m_data.m_move_assign; } void counter::operator()(std::string_view title) { m_records += fmt::format("{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}|{:9}| {}\n", - m_ctor, + m_data.m_ctor, static_default_ctor, - m_copy_ctor, - m_dtor + static_dtor, - m_assign, - m_swaps, - m_get, - m_const_get, - m_hash, - m_equals, - m_less, - m_move_ctor, - m_move_assign, + m_data.m_copy_ctor, + m_data.m_dtor + static_dtor, + m_data.m_assign, + m_data.m_swaps, + m_data.m_get, + m_data.m_const_get, + m_data.m_hash, + m_data.m_equals, + m_data.m_less, + m_data.m_move_ctor, + m_data.m_move_assign, total(), title); } diff --git a/test/app/counter.h b/test/app/counter.h index 984831cf..8eed498f 100644 --- a/test/app/counter.h +++ b/test/app/counter.h @@ -7,26 +7,35 @@ #include // for ostream #include // for allocator, string #include // for hash, string_view +#include class counter { - size_t m_ctor{}; - size_t m_default_ctor{}; - size_t m_copy_ctor{}; - size_t m_dtor{}; - size_t m_equals{}; - size_t m_less{}; - size_t m_assign{}; - size_t m_swaps{}; - size_t m_get{}; - size_t m_const_get{}; - size_t m_hash{}; - size_t m_move_ctor{}; - size_t m_move_assign{}; - - std::string m_records = - "\n ctor defctor cpyctor dtor assign swaps get cnstget hash equals less ctormv assignmv| total |\n"; - public: + struct data_t { + size_t m_ctor{}; + size_t m_default_ctor{}; + size_t m_copy_ctor{}; + size_t m_dtor{}; + size_t m_assign{}; + size_t m_swaps{}; + size_t m_get{}; + size_t m_const_get{}; + size_t m_hash{}; + size_t m_equals{}; + size_t m_less{}; + size_t m_move_ctor{}; + size_t m_move_assign{}; + + friend auto operator==(data_t const& a, data_t const& b) -> bool { + static_assert(std::has_unique_object_representations_v); + return 0 == std::memcmp(&a, &b, sizeof(data_t)); + } + + friend auto operator!=(data_t const& a, data_t const& b) -> bool { + return !(a == b); + } + }; + counter(counter const&) = delete; counter(counter&&) = delete; auto operator=(counter const&) -> counter& = delete; @@ -65,55 +74,59 @@ class counter { void check_all_done() const; [[nodiscard]] auto ctor() const -> size_t { - return m_ctor; + return m_data.m_ctor; } [[nodiscard]] auto default_ctor() const -> size_t { - return m_default_ctor; + return m_data.m_default_ctor; } [[nodiscard]] auto copy_ctor() const -> size_t { - return m_copy_ctor; + return m_data.m_copy_ctor; } [[nodiscard]] auto dtor() const -> size_t { - return m_dtor; + return m_data.m_dtor; } [[nodiscard]] auto equals() const -> size_t { - return m_equals; + return m_data.m_equals; } [[nodiscard]] auto less() const -> size_t { - return m_less; + return m_data.m_less; } [[nodiscard]] auto assign() const -> size_t { - return m_assign; + return m_data.m_assign; } [[nodiscard]] auto swaps() const -> size_t { - return m_swaps; + return m_data.m_swaps; } [[nodiscard]] auto get() const -> size_t { - return m_get; + return m_data.m_get; } [[nodiscard]] auto const_get() const -> size_t { - return m_const_get; + return m_data.m_const_get; } [[nodiscard]] auto hash() const -> size_t { - return m_hash; + return m_data.m_hash; } [[nodiscard]] auto move_ctor() const -> size_t { - return m_move_ctor; + return m_data.m_move_ctor; } [[nodiscard]] auto move_assign() const -> size_t { - return m_move_assign; + return m_data.m_move_assign; + } + + [[nodiscard]] auto data() const -> data_t const& { + return m_data; } friend auto operator<<(std::ostream& os, counter const& c) -> std::ostream&; @@ -124,6 +137,12 @@ class counter { static size_t static_default_ctor; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static size_t static_dtor; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +private: + data_t m_data{}; + + std::string m_records = + "\n ctor defctor cpyctor dtor assign swaps get cnstget hash equals less ctormv assignmv| total |\n"; }; // Throws an exception, this overload should never be taken! diff --git a/test/app/counting_allocator.h b/test/app/counting_allocator.h new file mode 100644 index 00000000..64129a51 --- /dev/null +++ b/test/app/counting_allocator.h @@ -0,0 +1,138 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +// Source: https://github.com/bitcoin/bitcoin/blob/master/src/memusage.h#L41-L61 +static inline auto malloc_usage(size_t alloc) -> size_t { + static_assert(sizeof(void*) == 8 || sizeof(void*) == 4); + + // Measured on libc6 2.19 on Linux. + if constexpr (sizeof(void*) == 8U) { + return ((alloc + 31U) >> 4U) << 4U; + } else { + return ((alloc + 15U) >> 3U) << 3U; + } +} + +class counts_for_allocator { + struct measurement_internal { + std::chrono::steady_clock::time_point m_tp{}; + size_t m_diff{}; + }; + + struct measurement { + std::chrono::steady_clock::duration m_duration{}; + size_t m_num_bytes_allocated{}; + }; + + std::vector m_measurements{}; + std::chrono::steady_clock::time_point m_start = std::chrono::steady_clock::now(); + + template + void each_measurement(Op op) const { + auto total_bytes = size_t(); + auto const start_time = m_start; + for (auto const& m : m_measurements) { + bool is_add = true; + size_t bytes = m.m_diff; + if (bytes > (0U - bytes)) { + // negative number + is_add = false; + bytes = 0U - bytes; + } + + if (is_add) { + total_bytes += malloc_usage(bytes); + } else { + total_bytes -= malloc_usage(bytes); + } + op(measurement{m.m_tp - start_time, total_bytes}); + } + } + +public: + void add(size_t count) { + m_measurements.emplace_back(measurement_internal{std::chrono::steady_clock::now(), count}); + } + + void sub(size_t count) { + // overflow, but it's ok + m_measurements.emplace_back(measurement_internal{std::chrono::steady_clock::now(), 0U - count}); + } + + void save(std::filesystem::path const& filename) const { + auto fout = std::ofstream(filename); + each_measurement([&](measurement m) { + fmt::print(fout, "{}; {}\n", std::chrono::duration(m.m_duration).count(), m.m_num_bytes_allocated); + }); + } + + [[nodiscard]] auto size() const -> size_t { + return m_measurements.size(); + } + + void reset() { + m_measurements.clear(); + m_start = std::chrono::steady_clock::now(); + } +}; + +/** + * Forwards all allocations/deallocations to the counts + */ +template +class counting_allocator { + counts_for_allocator* m_counts; + + template + friend class counting_allocator; + +public: + using value_type = T; + + /** + * Not explicit so we can easily construct it with the correct resource + */ + counting_allocator(counts_for_allocator* counts) noexcept // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) + : m_counts(counts) {} + + /** + * Not explicit so we can easily construct it with the correct resource + */ + template + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) + counting_allocator(counting_allocator const& other) noexcept + : m_counts(other.m_counts) {} + + counting_allocator(counting_allocator const& other) noexcept = default; + counting_allocator(counting_allocator&& other) noexcept = default; + auto operator=(counting_allocator const& other) noexcept -> counting_allocator& = default; + auto operator=(counting_allocator&& other) noexcept -> counting_allocator& = default; + ~counting_allocator() = default; + + auto allocate(size_t n) -> T* { + m_counts->add(sizeof(T) * n); + return std::allocator{}.allocate(n); + } + + void deallocate(T* p, size_t n) noexcept { + m_counts->sub(sizeof(T) * n); + std::allocator{}.deallocate(p, n); + } + + template + friend auto operator==(counting_allocator const& a, counting_allocator const& b) noexcept -> bool { + return a.m_counts == b.m_counts; + } + + template + friend auto operator!=(counting_allocator const& a, counting_allocator const& b) noexcept -> bool { + return a.m_counts != b.m_counts; + } +}; diff --git a/test/app/doctest.h b/test/app/doctest.h new file mode 100644 index 00000000..0a44fd23 --- /dev/null +++ b/test/app/doctest.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include + +#include + +template , + class KeyEqual = std::equal_to, + class AllocatorOrContainer = std::deque>, + class Bucket = ankerl::unordered_dense::bucket_type::standard> +using deque_map = ankerl::unordered_dense::detail::table; + +template , + class KeyEqual = std::equal_to, + class AllocatorOrContainer = std::deque, + class Bucket = ankerl::unordered_dense::bucket_type::standard> +using deque_set = ankerl::unordered_dense::detail::table; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TEST_CASE_MAP(name, ...) \ + TEST_CASE_TEMPLATE(name, \ + map_t, \ + ankerl::unordered_dense::map<__VA_ARGS__>, \ + ankerl::unordered_dense::segmented_map<__VA_ARGS__>, \ + deque_map<__VA_ARGS__>) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TEST_CASE_SET(name, ...) \ + TEST_CASE_TEMPLATE(name, \ + set_t, \ + ankerl::unordered_dense::set<__VA_ARGS__>, \ + ankerl::unordered_dense::segmented_set<__VA_ARGS__>, \ + deque_set<__VA_ARGS__>) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TYPE_TO_STRING_MAP(...) \ + TYPE_TO_STRING(ankerl::unordered_dense::map<__VA_ARGS__>); /*NOLINT*/ \ + TYPE_TO_STRING(ankerl::unordered_dense::segmented_map<__VA_ARGS__>) /*NOLINT*/ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TYPE_TO_STRING_SET(...) \ + TYPE_TO_STRING(ankerl::unordered_dense::set<__VA_ARGS__>); /*NOLINT*/ \ + TYPE_TO_STRING(ankerl::unordered_dense::segmented_set<__VA_ARGS__>) /*NOLINT*/ + +#if defined(ANKERL_UNORDERED_DENSE_PMR) + +// unfortunately there's no std::experimental::pmr::deque on macos, so just skip this here + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define TEST_CASE_PMR_MAP(name, ...) \ + TEST_CASE_TEMPLATE(name, \ + map_t, \ + ankerl::unordered_dense::pmr::map<__VA_ARGS__>, \ + ankerl::unordered_dense::pmr::segmented_map<__VA_ARGS__>) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define TEST_CASE_PMR_SET(name, ...) \ + TEST_CASE_TEMPLATE(name, \ + set_t, \ + ankerl::unordered_dense::pmr::set<__VA_ARGS__>, \ + ankerl::unordered_dense::pmr::segmented_set<__VA_ARGS__>) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define TYPE_TO_STRING_PMR_MAP(...) \ + TYPE_TO_STRING(ankerl::unordered_dense::pmr::map<__VA_ARGS__>); /*NOLINT*/ \ + TYPE_TO_STRING(ankerl::unordered_dense::pmr::segmented_map<__VA_ARGS__>) /*NOLINT*/ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define TYPE_TO_STRING_PMR_SET(...) \ + TYPE_TO_STRING(ankerl::unordered_dense::pmr::set<__VA_ARGS__>); /*NOLINT*/ \ + TYPE_TO_STRING(ankerl::unordered_dense::pmr::segmented_set<__VA_ARGS__>) /*NOLINT*/ + +#endif + +// adds the most important type to strings here + +TYPE_TO_STRING_MAP(counter::obj, counter::obj); +TYPE_TO_STRING_MAP(int, char const*); +TYPE_TO_STRING_MAP(int, int); +TYPE_TO_STRING_MAP(int, std::string); +TYPE_TO_STRING_MAP(std::string, size_t); +TYPE_TO_STRING_MAP(std::string, std::string); +TYPE_TO_STRING_MAP(uint64_t, uint64_t); +TYPE_TO_STRING_MAP(uint32_t, int); +TYPE_TO_STRING_MAP(uint64_t, int); +TYPE_TO_STRING_SET(counter::obj); +TYPE_TO_STRING_SET(int); +TYPE_TO_STRING_SET(std::string); +TYPE_TO_STRING_SET(uint32_t); +TYPE_TO_STRING_SET(uint64_t); diff --git a/test/bench/copy.cpp b/test/bench/copy.cpp index e70fb7cf..1ac08835 100644 --- a/test/bench/copy.cpp +++ b/test/bench/copy.cpp @@ -2,8 +2,8 @@ #include // for Rng, Bench -#include // for TestCase, skip, TEST_CASE, test_... -#include // for format +#include // for TestCase, skip, TEST_CASE, test_... +#include // for format #include // for size_t #include // for uint64_t @@ -38,8 +38,8 @@ TEST_CASE("bench_copy_rhf" * doctest::test_suite("bench") * doctest::skip()) { #endif -TEST_CASE("bench_copy_udm" * doctest::test_suite("bench") * doctest::skip()) { - bench>("ankerl::unordered_dense::map"); +TEST_CASE_MAP("bench_copy_udm" * doctest::test_suite("bench") * doctest::skip(), uint64_t, uint64_t) { + bench("ankerl::unordered_dense::map"); } TEST_CASE("bench_copy_std" * doctest::test_suite("bench") * doctest::skip()) { diff --git a/test/bench/find_random.cpp b/test/bench/find_random.cpp index 035ed3f2..d3f7ae4e 100644 --- a/test/bench/find_random.cpp +++ b/test/bench/find_random.cpp @@ -3,8 +3,8 @@ #include // for name_of_type #include // for Rng -#include // for TestCase, skip, ResultBuilder -#include // for format, print +#include // for TestCase, skip, ResultBuilder +#include // for format, print #include // for fill_n #include // for array @@ -17,11 +17,11 @@ template void bench() { static constexpr size_t num_total = 4; - auto requiredChecksum = std::array{200000, 25198620, 50197240, 75195862, 100194482}; + auto required_checksum = std::array{200000, 25198620, 50197240, 75195862, 100194482}; auto total = std::chrono::steady_clock::duration(); - for (size_t numFound = 0; numFound < 5; ++numFound) { - auto title = fmt::format("random find {}% success {}", numFound * 100 / num_total, name_of_type()); + for (size_t num_found = 0; num_found < 5; ++num_found) { + auto title = fmt::format("random find {}% success {}", num_found * 100 / num_total, name_of_type()); auto rng = ankerl::nanobench::Rng(123); size_t checksum = 0; @@ -29,7 +29,7 @@ void bench() { using ary_t = std::array; auto insert_random = ary_t(); insert_random.fill(true); - for (typename ary_t::size_type i = 0; i < numFound; ++i) { + for (typename ary_t::size_type i = 0; i < num_found; ++i) { insert_random[i] = false; } @@ -49,7 +49,7 @@ void bench() { do { // insert numTotal entries: some random, some sequential. rng.shuffle(insert_random); - for (bool is_random_to_insert : insert_random) { + for (bool const is_random_to_insert : insert_random) { auto val = another_unrelated_rng(); if (is_random_to_insert) { map[static_cast(rng())] = static_cast(1); @@ -76,7 +76,7 @@ void bench() { total += after - before; fmt::print("{}s {}\n", std::chrono::duration(after - before).count(), title); } - REQUIRE(checksum == requiredChecksum[numFound]); + REQUIRE(checksum == required_checksum[num_found]); } fmt::print("{}s total\n", std::chrono::duration(total).count()); } @@ -96,6 +96,6 @@ TEST_CASE("bench_find_random_rh" * doctest::test_suite("bench") * doctest::skip( #endif // 8.87 -TEST_CASE("bench_find_random_udm" * doctest::test_suite("bench") * doctest::skip()) { - bench>(); +TEST_CASE_MAP("bench_find_random_udm" * doctest::test_suite("bench") * doctest::skip(), size_t, size_t) { + bench(); } diff --git a/test/bench/game_of_life.cpp b/test/bench/game_of_life.cpp new file mode 100644 index 00000000..a005a50f --- /dev/null +++ b/test/bench/game_of_life.cpp @@ -0,0 +1,170 @@ +#include // for map + +#include + +#include // for TestCase, skip, ResultBuilder +#include // for format, print + +#include +#include + +#if __has_include("boost/unordered/unordered_flat_map.hpp") +# pragma clang diagnostic ignored "-Wold-style-cast" +# include "boost/unordered/unordered_flat_map.hpp" +# define HAS_BOOST_UNORDERED_FLAT_MAP() 1 // NOLINT(cppcoreguidelines-macro-usage) +#else +# define HAS_BOOST_UNORDERED_FLAT_MAP() 0 // NOLINT(cppcoreguidelines-macro-usage) +#endif + +#if 0 && __has_include("absl/container/flat_hash_map.h") +# pragma clang diagnostic ignored "-Wdeprecated-builtins" +# pragma clang diagnostic ignored "-Wsign-conversion" +# include +# define HAS_ABSL() 1 // NOLINT(cppcoreguidelines-macro-usage) +#else +# define HAS_ABSL() 0 // NOLINT(cppcoreguidelines-macro-usage) +#endif + +class vec2 { + uint32_t m_xy; + +public: + constexpr vec2(uint16_t x, uint16_t y) + : m_xy{static_cast(x) << 16U | y} {} + + constexpr explicit vec2(uint32_t xy) + : m_xy(xy) {} + + [[nodiscard]] constexpr auto pack() const -> uint32_t { + return m_xy; + }; + + [[nodiscard]] constexpr auto add_x(uint16_t x) const -> vec2 { + return vec2{m_xy + (static_cast(x) << 16U)}; + } + + [[nodiscard]] constexpr auto add_y(uint16_t y) const -> vec2 { + return vec2{(m_xy & 0xffff0000) | ((m_xy + y) & 0xffff)}; + } + + template + constexpr void for_each_surrounding(Op&& op) const { + uint32_t v = m_xy; + + uint32_t upper = (v & 0xffff0000U) - 0x10000; + uint32_t l1 = (v - 1) & 0xffffU; + uint32_t l2 = v & 0xffffU; + uint32_t l3 = (v + 1) & 0xffffU; + + op(upper | l1); + op(upper | l2); + op(upper | l3); + + upper += 0x10000; + op(upper | l1); + // op(upper | l2); + op(upper | l3); + + upper += 0x10000; + op(upper | l1); + op(upper | l2); + op(upper | l3); + } +}; + +template +auto game_of_life(std::string_view name, size_t nsteps, Map map1, std::vector state) -> size_t { + auto before = std::chrono::steady_clock::now(); + map1.clear(); + auto map2 = map1; // copy the empty map so we get the allocator + + for (auto& v : state) { + v = v.add_x(UINT16_MAX / 2).add_y(UINT16_MAX / 2); + map1[v.pack()] = true; + v.for_each_surrounding([&](uint32_t xy) { + map1.emplace(xy, false); + }); + } + + auto* m1 = &map1; + auto* m2 = &map2; + for (size_t i = 0; i < nsteps; ++i) { + for (auto const& kv : *m1) { + auto const& pos = kv.first; + auto alive = kv.second; + int neighbors = 0; + vec2{pos}.for_each_surrounding([&](uint32_t xy) { + if (auto x = m1->find(xy); x != m1->end()) { + neighbors += x->second; + } + }); + if ((alive && (neighbors == 2 || neighbors == 3)) || (!alive && neighbors == 3)) { + (*m2)[pos] = true; + vec2{pos}.for_each_surrounding([&](uint32_t xy) { + m2->emplace(xy, false); + }); + } + } + m1->clear(); + std::swap(m1, m2); + } + + size_t final_population = 0; + for (auto const& kv : *m1) { + final_population += kv.second; + } + auto after = std::chrono::steady_clock::now(); + fmt::print("{}s {}\n", std::chrono::duration(after - before).count(), name); + return final_population; +} + +TEST_CASE("gameoflife_gotts-dots" * doctest::test_suite("bench") * doctest::skip()) { + // https://conwaylife.com/wiki/Gotts_dots + auto state = std::vector{ + {0, 0}, {0, 1}, {0, 2}, // 1 + {4, 11}, {5, 12}, {6, 13}, {7, 12}, {8, 11}, // 2 + {9, 13}, {9, 14}, {9, 15}, // 3 + {185, 24}, {186, 25}, {186, 26}, {186, 27}, {185, 27}, {184, 27}, {183, 27}, {182, 26}, // 4 + {179, 28}, {180, 29}, {181, 29}, {179, 30}, // 5 + {182, 32}, {183, 31}, {184, 31}, {185, 31}, {186, 31}, {186, 32}, {186, 33}, {185, 34}, // 6 + {175, 35}, {176, 36}, {170, 37}, {176, 37}, {171, 38}, {172, 38}, {173, 38}, {174, 38}, {175, 38}, {176, 38}, // 7 + }; + + // size_t nsteps = 200; + // size_t nsteps = 2000; + size_t nsteps = 4000; + // size_t nsteps = 10000; + + auto pop = size_t(); + { + using map_t = ankerl::unordered_dense::map; + pop = game_of_life("ankerl::unordered_dense::map", nsteps, map_t(), state); + } + { + using map_t = ankerl::unordered_dense::segmented_map; + auto new_pop = game_of_life("ankerl::unordered_dense::segmented_map", nsteps, map_t(), state); + REQUIRE(pop == new_pop); + } + +#if HAS_BOOST_UNORDERED_FLAT_MAP + { + using map_t = boost::unordered_flat_map; + auto new_pop = game_of_life("boost::unordered_flat_map", nsteps, map_t(), state); + REQUIRE(pop == new_pop); + } +#endif + +#if HAS_ABSL() + { + using map_t = absl::flat_hash_map; + auto new_pop = game_of_life("absl::flat_hash_map", nsteps, map_t(), state); + REQUIRE(pop == new_pop); + } +#endif + + { + using map_t = std::unordered_map; + auto new_pop = game_of_life("std::unordered_map", nsteps, map_t(), state); + REQUIRE(pop == new_pop); + } +} diff --git a/test/bench/quick_overall_map.cpp b/test/bench/quick_overall_map.cpp index aa66613e..c64ca9a0 100644 --- a/test/bench/quick_overall_map.cpp +++ b/test/bench/quick_overall_map.cpp @@ -9,6 +9,7 @@ #include // for duration, operator-, high_resolu... #include // for uint64_t #include // for size_t, memcpy +#include // for deque #include // for string, basic_string, operator== #include // for string_view, literals #include // for unordered_map, operator!= @@ -67,7 +68,7 @@ void bench_random_insert_erase(ankerl::nanobench::Bench* bench, std::string_view // iterate template void bench_iterate(ankerl::nanobench::Bench* bench, std::string_view name) { - size_t num_elements = 5000; + size_t const num_elements = 5000; auto key = init_key(); @@ -197,6 +198,14 @@ TEST_CASE("bench_quick_overall_rhn" * doctest::test_suite("bench") * doctest::sk #endif +using hash_t = ankerl::unordered_dense::hash; +using eq_t = std::equal_to; +using pair_t = std::pair; + +using hash_str_t = ankerl::unordered_dense::hash; +using eq_str_t = std::equal_to; +using pair_str_t = std::pair; + TEST_CASE("bench_quick_overall_std" * doctest::test_suite("bench") * doctest::skip()) { ankerl::nanobench::Bench bench; bench_all>(&bench, "std::unordered_map"); @@ -207,30 +216,59 @@ TEST_CASE("bench_quick_overall_std" * doctest::test_suite("bench") * doctest::sk TEST_CASE("bench_quick_overall_udm" * doctest::test_suite("bench") * doctest::skip()) { ankerl::nanobench::Bench bench; // bench.minEpochTime(1s); - bench_all>(&bench, "ankerl::unordered_dense::map"); - bench_all>>( - &bench, "ankerl::unordered_dense::map"); + + using map_t = ankerl::unordered_dense::map; + bench_all(&bench, "ankerl::unordered_dense::map"); + + using map_str_t = ankerl::unordered_dense::map; + bench_all(&bench, "ankerl::unordered_dense::map"); + fmt::print("{} bench_quick_overall_map_udm\n", geomean1(bench)); } +TEST_CASE("bench_quick_overall_segmented_vector" * doctest::test_suite("bench") * doctest::skip()) { + ankerl::nanobench::Bench bench; + // bench.minEpochTime(1s); + using vec_t = ankerl::unordered_dense::segmented_vector; + using map_t = ankerl::unordered_dense::segmented_map; + bench_all(&bench, "ankerl::unordered_dense::map segmented_vector"); + + using vec_str_t = ankerl::unordered_dense::segmented_vector; + using map_str_t = ankerl::unordered_dense::map; + bench_all(&bench, "ankerl::unordered_dense::map segmented_vector"); + + fmt::print("{} bench_quick_overall_segmented_vector\n", geomean1(bench)); +} + +TEST_CASE("bench_quick_overall_deque" * doctest::test_suite("bench") * doctest::skip()) { + ankerl::nanobench::Bench bench; + // bench.minEpochTime(1s); + + using vec_t = std::deque; + using map_t = ankerl::unordered_dense::map; + bench_all(&bench, "ankerl::unordered_dense::map deque"); + + using vec_str_t = std::deque; + using map_str_t = ankerl::unordered_dense::map; + bench_all(&bench, "ankerl::unordered_dense::map deque"); + + fmt::print("{} bench_quick_overall_deque\n", geomean1(bench)); +} + TEST_CASE("bench_quick_overall_udm_bigbucket" * doctest::test_suite("bench") * doctest::skip()) { + using bucket_t = ankerl::unordered_dense::bucket_type::big; + ankerl::nanobench::Bench bench; // bench.minEpochTime(1s); - bench_all, - std::equal_to, - std::allocator>, - ankerl::unordered_dense::bucket_type::big>>( - &bench, "ankerl::unordered_dense::map"); - - bench_all, - std::equal_to, - std::allocator>, - ankerl::unordered_dense::bucket_type::big>>( - &bench, "ankerl::unordered_dense::map"); + + using alloc_t = std::allocator; + using map_t = ankerl::unordered_dense::map; + bench_all(&bench, "ankerl::unordered_dense::map"); + + using alloc_str_t = std::allocator; + using map_str_t = ankerl::unordered_dense::map; + bench_all(&bench, "ankerl::unordered_dense::map"); + fmt::print("{} bench_quick_overall_map_udm\n", geomean1(bench)); } diff --git a/test/bench/show_allocations.cpp b/test/bench/show_allocations.cpp new file mode 100644 index 00000000..55365a06 --- /dev/null +++ b/test/bench/show_allocations.cpp @@ -0,0 +1,112 @@ +// #include "absl/container/flat_hash_map.h" +#include // for map, operator== + +#include + +#include + +#if __has_include("boost/unordered/unordered_flat_map.hpp") +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wold-style-cast" +# include "boost/unordered/unordered_flat_map.hpp" +# define HAS_BOOST_UNORDERED_FLAT_MAP() 1 // NOLINT(cppcoreguidelines-macro-usage) +#else +# define HAS_BOOST_UNORDERED_FLAT_MAP() 0 // NOLINT(cppcoreguidelines-macro-usage) +#endif + +#include +#include + +#include +#include +#include +#include + +template +void evaluate_map(Map& map) { + auto rng = ankerl::nanobench::Rng{1234}; + + auto num_elements = size_t{10'000'000}; + for (uint64_t i = 0; i < num_elements; ++i) { + map[rng()] = i; + } + REQUIRE(map.size() == num_elements); +} + +using hash_t = ankerl::unordered_dense::hash; +using eq_t = std::equal_to; +using pair_t = std::pair; +using pair_const_t = std::pair; +using alloc_t = counting_allocator; +using alloc_const_t = counting_allocator; + +TEST_CASE("allocated_memory_std_vector" * doctest::skip()) { + auto counters = counts_for_allocator{}; + { + using vec_t = std::vector; + using map_t = ankerl::unordered_dense::map; + auto map = map_t(0, hash_t{}, eq_t{}, alloc_t{&counters}); + evaluate_map(map); + } + counters.save("allocated_memory_std_vector.txt"); +} + +#if HAS_BOOST_UNORDERED_FLAT_MAP() + +TEST_CASE("allocated_memory_boost_flat_map" * doctest::skip()) { + auto counters = counts_for_allocator{}; + { + using map_t = boost::unordered_flat_map; + auto map = map_t(alloc_t{&counters}); + evaluate_map(map); + } + counters.save("allocated_memory_unordered_flat_map.txt"); +} +#endif + +TEST_CASE("allocated_memory_std_deque" * doctest::skip()) { + auto counters = counts_for_allocator{}; + { + using vec_t = std::deque; + using map_t = ankerl::unordered_dense::map; + auto map = map_t(0, hash_t{}, eq_t{}, alloc_t{&counters}); + evaluate_map(map); + } + counters.save("allocated_memory_std_deque.txt"); +} + +TEST_CASE("allocated_memory_segmented_vector" * doctest::skip()) { + auto counters = counts_for_allocator{}; + { + using vec_t = ankerl::unordered_dense::segmented_vector; + using map_t = ankerl::unordered_dense::segmented_map; + auto map = map_t{0, hash_t{}, eq_t{}, alloc_t{&counters}}; + evaluate_map(map); + } + counters.save("allocated_memory_segmented_vector.txt"); +} + +#if 0 + +TEST_CASE("allocated_memory_std_unordered_map" * doctest::skip()) { + auto counters = counts_for_allocator{}; + { + using map_t = std::unordered_map; + auto map = map_t(0, alloc_t{&counters}); + evaluate_map(map); + } + counters.save("allocated_memory_std_unordered_map.txt"); +} + +TEST_CASE("allocated_memory_boost_unordered_flat_map" * doctest::skip()) { + auto counters = counts_for_allocator{}; + { + using map_t = absl:: + flat_hash_map, eq_t, alloc_const_t>; + auto map = map_t(0, alloc_t{&counters}); + evaluate_map(map); + } + counters.save("allocated_memory_absl_flat_hash_map.txt"); +} + +#endif \ No newline at end of file diff --git a/test/fuzz/api.cpp b/test/fuzz/api.cpp index 53da156a..0f51e6e0 100644 --- a/test/fuzz/api.cpp +++ b/test/fuzz/api.cpp @@ -8,6 +8,7 @@ # include #endif +#include // for deque #include // for initializer_list #include // for distance, __iterator_traits<>::d... #include // for operator new @@ -15,13 +16,14 @@ #include // for swap, pair, piecewise_construct #include // for vector -namespace fuzz { +namespace { +template void api(uint8_t const* data, size_t size) { + using map_t = Map; auto p = fuzz::provider(data, size); auto counts = counter(); - using map_t = ankerl::unordered_dense::map; auto map = map_t(); p.repeat_oneof( [&] { @@ -120,18 +122,38 @@ void api(uint8_t const* data, size_t size) { new (&map) map_t(); }, [&] { - std::erase_if(map, [&](map_t::value_type const& /*v*/) { + std::erase_if(map, [&](typename map_t::value_type const& /*v*/) { return p.integral(); }); }); } +} // namespace + +namespace fuzz { + +void api_map(uint8_t const* data, size_t size) { + api>(data, size); +} + +void api_segmented_map(uint8_t const* data, size_t size) { + api>(data, size); +} + +void api_deque_map(uint8_t const* data, size_t size) { + api, + std::equal_to, + std::deque>>>(data, size); +} + } // namespace fuzz #if defined(FUZZ) // NOLINTNEXTLINE(readability-identifier-naming) extern "C" auto LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -> int { - fuzz::api(data, size); + fuzz::api_segmented_map(data, size); return 0; } #endif diff --git a/test/fuzz/api.h b/test/fuzz/api.h index 0e4ee6bb..6db59d56 100644 --- a/test/fuzz/api.h +++ b/test/fuzz/api.h @@ -5,6 +5,8 @@ namespace fuzz { -void api(uint8_t const* data, size_t size); +void api_map(uint8_t const* data, size_t size); +void api_segmented_map(uint8_t const* data, size_t size); +void api_deque_map(uint8_t const* data, size_t size); } // namespace fuzz diff --git a/test/fuzz/insert_erase.cpp b/test/fuzz/insert_erase.cpp index 1a65aecb..d01076fd 100644 --- a/test/fuzz/insert_erase.cpp +++ b/test/fuzz/insert_erase.cpp @@ -9,6 +9,7 @@ #include // for size_t #include // for uint64_t, uint8_t +#include // for deque #include // for equal_to #include // for __iter_val_t, __iter_key_t #include // for unordered_map, operator==, hash @@ -25,14 +26,11 @@ struct dummy_hash { } }; -} // namespace - -namespace fuzz { - +template void insert_erase(uint8_t const* data, size_t size) { auto p = fuzz::provider(data, size); - auto ank = ankerl::unordered_dense::map(); + auto ank = Map(); auto ref = std::unordered_map(); auto c = uint64_t(); @@ -50,12 +48,29 @@ void insert_erase(uint8_t const* data, size_t size) { REQUIRE(cpy == ref); } +} // namespace + +namespace fuzz { + +void insert_erase_map(uint8_t const* data, size_t size) { + insert_erase>(data, size); +} +void insert_erase_segmented_map(uint8_t const* data, size_t size) { + insert_erase>(data, size); +} +void insert_erase_deque_map(uint8_t const* data, size_t size) { + insert_erase, + std::equal_to, + std::deque>>>(data, size); +} } // namespace fuzz #if defined(FUZZ) // NOLINTNEXTLINE(readability-identifier-naming) extern "C" auto LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -> int { - fuzz::insert_erase(data, size); + fuzz::insert_erase_map(data, size); return 0; } #endif \ No newline at end of file diff --git a/test/fuzz/insert_erase.h b/test/fuzz/insert_erase.h index 7cd434a7..1a1e615b 100644 --- a/test/fuzz/insert_erase.h +++ b/test/fuzz/insert_erase.h @@ -5,6 +5,8 @@ namespace fuzz { -void insert_erase(uint8_t const* data, size_t size); +void insert_erase_map(uint8_t const* data, size_t size); +void insert_erase_segmented_map(uint8_t const* data, size_t size); +void insert_erase_deque_map(uint8_t const* data, size_t size); } // namespace fuzz diff --git a/test/fuzz/replace.cpp b/test/fuzz/replace.cpp index 3066cbfe..5838991e 100644 --- a/test/fuzz/replace.cpp +++ b/test/fuzz/replace.cpp @@ -8,25 +8,27 @@ # include #endif +#include #include -namespace fuzz { +namespace { +template void replace(uint8_t const* data, size_t size) { auto p = fuzz::provider{data, size}; auto counts = counter{}; - using map_t = ankerl::unordered_dense::map; + using map_t = Map; auto initial_size = p.bounded(100); - auto map = ankerl::unordered_dense::map{}; + auto map = map_t{}; for (size_t i = 0; i < initial_size; ++i) { map.try_emplace(counter::obj{i, counts}, counter::obj{i, counts}); } // create a container with data in it provided by fuzzer - auto container = map_t::value_container_type{}; + auto container = typename map_t::value_container_type{}; auto comparison_container = std::vector>(); auto v = size_t{}; while (p.has_remaining_bytes()) { @@ -63,12 +65,32 @@ void replace(uint8_t const* data, size_t size) { } } +} // namespace + +namespace fuzz { + +void replace_map(uint8_t const* data, size_t size) { + replace>(data, size); +} + +void replace_segmented_map(uint8_t const* data, size_t size) { + replace>(data, size); +} + +void replace_deque_map(uint8_t const* data, size_t size) { + replace, + std::equal_to, + std::deque>>>(data, size); +} + } // namespace fuzz #if defined(FUZZ) // NOLINTNEXTLINE(readability-identifier-naming) extern "C" auto LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -> int { - fuzz::replace(data, size); + fuzz::replace_map(data, size); return 0; } #endif \ No newline at end of file diff --git a/test/fuzz/replace.h b/test/fuzz/replace.h index be3a13b4..c8eada63 100644 --- a/test/fuzz/replace.h +++ b/test/fuzz/replace.h @@ -5,6 +5,8 @@ namespace fuzz { -void replace(uint8_t const* data, size_t size); +void replace_map(uint8_t const* data, size_t size); +void replace_segmented_map(uint8_t const* data, size_t size); +void replace_deque_map(uint8_t const* data, size_t size); } // namespace fuzz diff --git a/test/fuzz/string.cpp b/test/fuzz/string.cpp index bf0fb67f..1db9a36e 100644 --- a/test/fuzz/string.cpp +++ b/test/fuzz/string.cpp @@ -8,17 +8,19 @@ #endif #include // for uint8_t +#include // for deque #include // for string, basic_string, operator== #include // for unordered_map, operator==, unord... #include // for pair #include // for vector -namespace fuzz { +namespace { +template void string(uint8_t const* data, size_t size) { auto p = fuzz::provider(data, size); - auto ank = ankerl::unordered_dense::map(); + auto ank = Map(); auto ref = std::unordered_map(); while (p.has_remaining_bytes()) { @@ -46,12 +48,31 @@ void string(uint8_t const* data, size_t size) { REQUIRE(std::unordered_map(ank.begin(), ank.end()) == ref); } +} // namespace + +namespace fuzz { + +void string_map(uint8_t const* data, size_t size) { + string>(data, size); +} + +void string_segmented_map(uint8_t const* data, size_t size) { + string>(data, size); +} +void string_deque_map(uint8_t const* data, size_t size) { + string, + std::equal_to, + std::deque>>>(data, size); +} + } // namespace fuzz #if defined(FUZZ) // NOLINTNEXTLINE(readability-identifier-naming) extern "C" auto LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -> int { - fuzz::string(data, size); + fuzz::string_map(data, size); return 0; } #endif \ No newline at end of file diff --git a/test/fuzz/string.h b/test/fuzz/string.h index 02c7948e..f4026e02 100644 --- a/test/fuzz/string.h +++ b/test/fuzz/string.h @@ -5,6 +5,8 @@ namespace fuzz { -void string(uint8_t const* data, size_t size); +void string_map(uint8_t const* data, size_t size); +void string_segmented_map(uint8_t const* data, size_t size); +void string_deque_map(uint8_t const* data, size_t size); } // namespace fuzz diff --git a/test/meson.build b/test/meson.build index 1dc59b7a..ae0361cb 100644 --- a/test/meson.build +++ b/test/meson.build @@ -7,10 +7,12 @@ test_sources = [ 'app/ui/progress_bar.cpp', 'app/unordered_dense.cpp', - 'bench/copy.cpp', - 'bench/find_random.cpp', - 'bench/quick_overall_map.cpp', 'bench/swap.cpp', + 'bench/show_allocations.cpp', + 'bench/quick_overall_map.cpp', + 'bench/game_of_life.cpp', + 'bench/find_random.cpp', + 'bench/copy.cpp', 'fuzz/api.cpp', 'fuzz/insert_erase.cpp', @@ -63,6 +65,7 @@ test_sources = [ 'unit/replace.cpp', 'unit/reserve_and_assign.cpp', 'unit/reserve.cpp', + 'unit/segmented_vector.cpp', 'unit/set_or_map_types.cpp', 'unit/set.cpp', 'unit/std_hash.cpp', @@ -93,6 +96,8 @@ foreach arg : [ '-pedantic-errors', '-Wold-style-cast', # '-Weffc++', doesn't work with fmt + + # '-march=native', ] if compiler.has_argument(arg) cpp_args += [arg] @@ -117,6 +122,8 @@ if get_option('cpp_args').contains('-m32') fmt_method = 'builtin' endif +# use e.g. +# CXX='ccache clang++' BOOST_ROOT=/home/martinus/dev/boost_1_81_0/ meson setup --buildtype release -Dcpp_std=c++17 build opt_boost = dependency('boost', required: false) link_args = [] if opt_boost.found() @@ -126,6 +133,13 @@ else add_global_arguments('-DANKERL_UNORDERED_DENSE_HAS_BOOST=0', language: 'cpp') endif +#opt_absl = dependency('absl_container', required: true, ) +#if opt_boost.found() +# add_global_arguments('-DANKERL_UNORDERED_DENSE_HAS_ABSL=1', language: 'cpp') +#else +# add_global_arguments('-DANKERL_UNORDERED_DENSE_HAS_ABSL=0', language: 'cpp') +#endif + test_exe = executable( 'udm', test_sources, @@ -138,7 +152,10 @@ test_exe = executable( # see what's in the [provide] sections for the dependency names dependency('doctest'), dependency('fmt', method: fmt_method), - opt_boost, # boost might not be found + + # disable these two if you don't want them + #dependency('boost'), + #dependency('absl_container', default_options: ['warning_level=0', 'werror=false']) ], ) diff --git a/test/unit/assign_to_move.cpp b/test/unit/assign_to_move.cpp index d4562eed..b22dffba 100644 --- a/test/unit/assign_to_move.cpp +++ b/test/unit/assign_to_move.cpp @@ -3,31 +3,31 @@ #define ENABLE_LOG_LINE #include -#include +#include #include // for remove_reference, remove_referen... #include // for move #include // for vector -TEST_CASE("assign_to_moved") { - auto a = ankerl::unordered_dense::map(); +TEST_CASE_MAP("assign_to_moved", int, int) { + auto a = map_t(); a[1] = 2; auto moved = std::move(a); REQUIRE(moved.size() == 1U); - auto c = ankerl::unordered_dense::map(); + auto c = map_t(); c[3] = 4; // assign to a moved map a = c; } -TEST_CASE("move_to_moved") { - auto a = ankerl::unordered_dense::map(); +TEST_CASE_MAP("move_to_moved", int, int) { + auto a = map_t(); a[1] = 2; auto moved = std::move(a); - auto c = ankerl::unordered_dense::map(); + auto c = map_t(); c[3] = 4; // assign to a moved map @@ -38,11 +38,11 @@ TEST_CASE("move_to_moved") { REQUIRE(moved[6] == 7); } -TEST_CASE("swap") { +TEST_CASE_MAP("swap", int, int) { { - auto b = ankerl::unordered_dense::map(); + auto b = map_t(); { - auto a = ankerl::unordered_dense::map(); + auto a = map_t(); b[1] = 2; a.swap(b); @@ -56,9 +56,9 @@ TEST_CASE("swap") { } { - auto a = ankerl::unordered_dense::map(); + auto a = map_t(); { - auto b = ankerl::unordered_dense::map(); + auto b = map_t(); b[1] = 2; a.swap(b); @@ -72,9 +72,9 @@ TEST_CASE("swap") { } { - auto a = ankerl::unordered_dense::map(); + auto a = map_t(); { - auto b = ankerl::unordered_dense::map(); + auto b = map_t(); a.swap(b); REQUIRE(a.end() == a.find(1)); REQUIRE(b.end() == b.find(1)); diff --git a/test/unit/assignment_combinations.cpp b/test/unit/assignment_combinations.cpp index ce186074..f8ce318b 100644 --- a/test/unit/assignment_combinations.cpp +++ b/test/unit/assignment_combinations.cpp @@ -1,45 +1,42 @@ #include #define ENABLE_LOG_LINE +#include #include -#include - #include // for uint64_t #include // for pair #include // for vector -using map_t = ankerl::unordered_dense::map; - -TEST_CASE("assignment_combinations_1") { +TEST_CASE_MAP("assignment_combinations_1", uint64_t, uint64_t) { map_t a; map_t b; b = a; REQUIRE(b == a); } -TEST_CASE("assignment_combinations_2") { +TEST_CASE_MAP("assignment_combinations_2", uint64_t, uint64_t) { map_t a; - map_t const& aConst = a; + map_t const& a_const = a; map_t b; a[123] = 321; b = a; REQUIRE(a.find(123)->second == 321); - REQUIRE(aConst.find(123)->second == 321); + REQUIRE(a_const.find(123)->second == 321); REQUIRE(b.find(123)->second == 321); a[123] = 111; REQUIRE(a.find(123)->second == 111); - REQUIRE(aConst.find(123)->second == 111); + REQUIRE(a_const.find(123)->second == 111); REQUIRE(b.find(123)->second == 321); b[123] = 222; REQUIRE(a.find(123)->second == 111); - REQUIRE(aConst.find(123)->second == 111); + REQUIRE(a_const.find(123)->second == 111); REQUIRE(b.find(123)->second == 222); } -TEST_CASE("assignment_combinations_3") { +TEST_CASE_MAP("assignment_combinations_3", uint64_t, uint64_t) { map_t a; map_t b; a[123] = 321; @@ -50,7 +47,7 @@ TEST_CASE("assignment_combinations_3") { REQUIRE(b.size() == 0); } -TEST_CASE("assignment_combinations_4") { +TEST_CASE_MAP("assignment_combinations_4", uint64_t, uint64_t) { map_t a; map_t b; b[123] = 321; @@ -60,7 +57,7 @@ TEST_CASE("assignment_combinations_4") { REQUIRE(b.size() == 0); } -TEST_CASE("assignment_combinations_5") { +TEST_CASE_MAP("assignment_combinations_5", uint64_t, uint64_t) { map_t a; map_t b; b[123] = 321; @@ -71,7 +68,7 @@ TEST_CASE("assignment_combinations_5") { REQUIRE(b.size() == 0); } -TEST_CASE("assignment_combinations_6") { +TEST_CASE_MAP("assignment_combinations_6", uint64_t, uint64_t) { map_t a; a[1] = 2; map_t b; @@ -87,7 +84,7 @@ TEST_CASE("assignment_combinations_6") { REQUIRE(b.find(1)->second == 2); } -TEST_CASE("assignment_combinations_7") { +TEST_CASE_MAP("assignment_combinations_7", uint64_t, uint64_t) { map_t a; a[1] = 2; a.clear(); @@ -99,7 +96,7 @@ TEST_CASE("assignment_combinations_7") { REQUIRE(a == b); } -TEST_CASE("assignment_combinations_7") { +TEST_CASE_MAP("assignment_combinations_7", uint64_t, uint64_t) { map_t a; a[1] = 2; map_t b; @@ -111,7 +108,7 @@ TEST_CASE("assignment_combinations_7") { REQUIRE(a == b); } -TEST_CASE("assignment_combinations_8") { +TEST_CASE_MAP("assignment_combinations_8", uint64_t, uint64_t) { map_t a; a[1] = 2; a.clear(); @@ -124,7 +121,7 @@ TEST_CASE("assignment_combinations_8") { REQUIRE(a == b); } -TEST_CASE("assignment_combinations_9") { +TEST_CASE_MAP("assignment_combinations_9", uint64_t, uint64_t) { map_t a; a[1] = 2; @@ -136,7 +133,7 @@ TEST_CASE("assignment_combinations_9") { REQUIRE(a.find(1) != a.end()); } -TEST_CASE("assignment_combinations_10") { +TEST_CASE_MAP("assignment_combinations_10", uint64_t, uint64_t) { map_t a; a[1] = 2; map_t b; diff --git a/test/unit/at.cpp b/test/unit/at.cpp index e7455f92..c7de73bc 100644 --- a/test/unit/at.cpp +++ b/test/unit/at.cpp @@ -1,12 +1,10 @@ #include -#include +#include #include // for out_of_range -using map_t = ankerl::unordered_dense::map; - -TEST_CASE("at") { +TEST_CASE_MAP("at", int, int) { map_t map; map_t const& cmap = map; diff --git a/test/unit/bucket.cpp b/test/unit/bucket.cpp index 22333a81..b862afe9 100644 --- a/test/unit/bucket.cpp +++ b/test/unit/bucket.cpp @@ -1,8 +1,8 @@ #include #include +#include -#include #include #include @@ -38,14 +38,20 @@ struct bucket_micro { uint8_t m_value_idx; }; -TEST_CASE("bucket_micro") { - using map_t = ankerl::unordered_dense::map, - std::equal_to, - std::allocator>, - bucket_micro>; +TYPE_TO_STRING_MAP(counter::obj, + counter::obj, + ankerl::unordered_dense::hash, + std::equal_to, + std::allocator>, + bucket_micro); +TEST_CASE_MAP("bucket_micro", + counter::obj, + counter::obj, + ankerl::unordered_dense::hash, + std::equal_to, + std::allocator>, + bucket_micro) { counter counts; INFO(counts); diff --git a/test/unit/contains.cpp b/test/unit/contains.cpp index 69e4404e..13da413d 100644 --- a/test/unit/contains.cpp +++ b/test/unit/contains.cpp @@ -1,11 +1,10 @@ #include -#include +#include #include // for uint64_t -TEST_CASE("contains") { - using map_t = ankerl::unordered_dense::map; +TEST_CASE_MAP("contains", uint64_t, uint64_t) { static_assert(std::is_same_v); auto map = map_t(); diff --git a/test/unit/copy_and_assign_maps.cpp b/test/unit/copy_and_assign_maps.cpp index 5213b6ba..186c4880 100644 --- a/test/unit/copy_and_assign_maps.cpp +++ b/test/unit/copy_and_assign_maps.cpp @@ -1,15 +1,12 @@ #include #define ENABLE_LOG_LINE +#include #include -#include - #include // for pair #include // for vector -using map_t = ankerl::unordered_dense::map; - // creates a map with some data in it template [[nodiscard]] auto create_map(int num_elements) -> M { @@ -20,61 +17,61 @@ template return m; } -TEST_CASE("copy_and_assign_maps_1") { +TEST_CASE_MAP("copy_and_assign_maps_1", int, int) { auto a = create_map(15); } -TEST_CASE("copy_and_assign_maps_2") { +TEST_CASE_MAP("copy_and_assign_maps_2", int, int) { auto a = create_map(100); } -TEST_CASE("copy_and_assign_maps_3") { +TEST_CASE_MAP("copy_and_assign_maps_3", int, int) { auto a = create_map(1); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) auto b = a; REQUIRE(a == b); } -TEST_CASE("copy_and_assign_maps_4") { +TEST_CASE_MAP("copy_and_assign_maps_4", int, int) { map_t a; REQUIRE(a.empty()); a.clear(); REQUIRE(a.empty()); } -TEST_CASE("copy_and_assign_maps_5") { +TEST_CASE_MAP("copy_and_assign_maps_5", int, int) { auto a = create_map(100); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) auto b = a; REQUIRE(b == a); } -TEST_CASE("copy_and_assign_maps_6") { +TEST_CASE_MAP("copy_and_assign_maps_6", int, int) { map_t a; a[123] = 321; a.clear(); - std::vector maps(10, a); + auto const maps = std::vector(10, a); for (auto const& map : maps) { REQUIRE(map.empty()); } } -TEST_CASE("copy_and_assign_maps_7") { - std::vector maps(10); +TEST_CASE_MAP("copy_and_assign_maps_7", int, int) { + auto const maps = std::vector(10); REQUIRE(maps.size() == 10U); } -TEST_CASE("copy_and_assign_maps_8") { +TEST_CASE_MAP("copy_and_assign_maps_8", int, int) { map_t a; - std::vector maps(12, a); + auto const maps = std::vector(12, a); REQUIRE(maps.size() == 12U); } -TEST_CASE("copy_and_assign_maps_9") { +TEST_CASE_MAP("copy_and_assign_maps_9", int, int) { map_t a; a[123] = 321; - std::vector maps(10, a); + auto const maps = std::vector(10, a); a[123] = 1; for (auto const& map : maps) { diff --git a/test/unit/copyassignment.cpp b/test/unit/copyassignment.cpp index 3796762e..2fc23f32 100644 --- a/test/unit/copyassignment.cpp +++ b/test/unit/copyassignment.cpp @@ -1,12 +1,12 @@ #include -#include +#include #include -TEST_CASE("copyassignment") { - auto map = ankerl::unordered_dense::map(); - auto tmp = ankerl::unordered_dense::map(); +TEST_CASE_MAP("copyassignment", std::string, std::string) { + auto map = map_t(); + auto tmp = map_t(); map.emplace("a", "b"); map = tmp; diff --git a/test/unit/count.cpp b/test/unit/count.cpp index 201dd17e..922b9118 100644 --- a/test/unit/count.cpp +++ b/test/unit/count.cpp @@ -1,9 +1,9 @@ #include -#include +#include -TEST_CASE("count") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("count", int, int) { + auto map = map_t(); REQUIRE(map.count(123) == 0); REQUIRE(map.count(0) == 0); map[123]; diff --git a/test/unit/ctors.cpp b/test/unit/ctors.cpp index 27e6351b..42e27906 100644 --- a/test/unit/ctors.cpp +++ b/test/unit/ctors.cpp @@ -1,8 +1,7 @@ #include -#include - #include +#include #include // for size_t #include // for pair @@ -31,12 +30,10 @@ class it { } }; -TEST_CASE("ctors_map") { - using map_t = ankerl::unordered_dense::map; - // using map_t = std::unordered_map; - using alloc_t = map_t::allocator_type; - using hash_t = map_t::hasher; - using key_eq_t = map_t::key_equal; +TEST_CASE_MAP("ctors_map", counter::obj, counter::obj) { + using alloc_t = typename map_t::allocator_type; + using hash_t = typename map_t::hasher; + using key_eq_t = typename map_t::key_equal; auto counts = counter(); INFO(counts); @@ -73,18 +70,19 @@ TEST_CASE("ctors_map") { } } -TEST_CASE("ctor_bucket_count") { +TEST_CASE_MAP("ctor_bucket_count_map", counter::obj, counter::obj) { { - auto m = ankerl::unordered_dense::map{}; + auto m = map_t{}; REQUIRE(m.bucket_count() == 0U); } { - auto m = ankerl::unordered_dense::map{150U}; + auto m = map_t{150U}; REQUIRE(m.bucket_count() == 256U); } - { - auto m = ankerl::unordered_dense::set{{1, 2, 3, 4}, 300U}; - REQUIRE(m.size() == 4U); - REQUIRE(m.bucket_count() == 512U); - } +} + +TEST_CASE_SET("ctor_bucket_count_set", int) { + auto m = ankerl::unordered_dense::set{{1, 2, 3, 4}, 300U}; + REQUIRE(m.size() == 4U); + REQUIRE(m.bucket_count() == 512U); } diff --git a/test/unit/custom_container.cpp b/test/unit/custom_container.cpp index 446f8cad..dd16cfdf 100644 --- a/test/unit/custom_container.cpp +++ b/test/unit/custom_container.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include @@ -10,10 +10,15 @@ static_assert( static_assert( !ankerl::unordered_dense::detail::is_detected_v>); -TEST_CASE("custom_container") { - using map_t = ankerl::unordered_dense:: - map, std::equal_to, std::deque>>; +TYPE_TO_STRING_MAP( + int, std::string, ankerl::unordered_dense::hash, std::equal_to, std::deque>); +TEST_CASE_MAP("custom_container", + int, + std::string, + ankerl::unordered_dense::hash, + std::equal_to, + std::deque>) { auto map = map_t(); for (int i = 0; i < 10; ++i) { @@ -21,10 +26,11 @@ TEST_CASE("custom_container") { } REQUIRE(std::is_same_v>, typename map_t::value_container_type>); - std::deque> container = std::move(map).extract(); + std::deque> const container = std::move(map).extract(); auto m2 = map_t(); - m2 = map; + // we allow use-after-move + m2 = map; // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved) auto map2 = map; std::swap(map2, map); diff --git a/test/unit/custom_container_boost.cpp b/test/unit/custom_container_boost.cpp index 00a4aaae..ca9352ee 100644 --- a/test/unit/custom_container_boost.cpp +++ b/test/unit/custom_container_boost.cpp @@ -1,66 +1,78 @@ #if ANKERL_UNORDERED_DENSE_HAS_BOOST +# if __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wold-style-cast" +# endif # include +# include + # include # include # include # include # include -# include # include -// See https://www.boost.org/doc/libs/1_80_0/doc/html/interprocess/allocators_containers.html -TEST_CASE("boost_container_vector") { - // Remove shared memory on construction and destruction - struct shm_remove { - shm_remove() { - boost::interprocess::shared_memory_object::remove("MySharedMemory"); - } - ~shm_remove() { - boost::interprocess::shared_memory_object::remove("MySharedMemory"); - } - - shm_remove(shm_remove const&) = delete; - shm_remove(shm_remove&&) = delete; - auto operator=(shm_remove const&) -> shm_remove = delete; - auto operator=(shm_remove&&) -> shm_remove = delete; - }; +// Alias an STL-like allocator of ints that allocates ints from the segment +using shmem_allocator = + boost::interprocess::allocator, boost::interprocess::managed_shared_memory::segment_manager>; +using shmem_vector = boost::interprocess::vector, shmem_allocator>; - auto remover = shm_remove(); +// Remove shared memory on construction and destruction +struct shm_remove { + shm_remove() { + boost::interprocess::shared_memory_object::remove("MySharedMemory"); + } + ~shm_remove() { + boost::interprocess::shared_memory_object::remove("MySharedMemory"); + } - // Create shared memory - auto segment = boost::interprocess::managed_shared_memory(boost::interprocess::create_only, "MySharedMemory", 65536); + shm_remove(shm_remove const&) = delete; + shm_remove(shm_remove&&) = delete; + auto operator=(shm_remove const&) -> shm_remove = delete; + auto operator=(shm_remove&&) -> shm_remove = delete; +}; - // Alias an STL-like allocator of ints that allocates ints from the segment - using shmem_allocator = boost::interprocess::allocator, - boost::interprocess::managed_shared_memory::segment_manager>; - using shmem_vector = boost::interprocess::vector, shmem_allocator>; +TYPE_TO_STRING_MAP(int, std::string, ankerl::unordered_dense::hash, std::equal_to, shmem_vector); - using map_t = - ankerl::unordered_dense::map, std::equal_to, shmem_vector>; +// See https://www.boost.org/doc/libs/1_80_0/doc/html/interprocess/allocators_containers.html +TEST_CASE_TEMPLATE( + "boost_container_vector", + map_t, + ankerl::unordered_dense::map, std::equal_to, shmem_vector>, + ankerl::unordered_dense:: + segmented_map, std::equal_to, shmem_allocator>) { + + auto remover = shm_remove(); + // Create shared memory + auto segment = boost::interprocess::managed_shared_memory(boost::interprocess::create_only, "MySharedMemory", 1024 * 1024); auto map = map_t{shmem_allocator{segment.get_segment_manager()}}; - for (int i = 0; i < 100; ++i) { + int total = 10000; + + for (int i = 0; i < total; ++i) { map.try_emplace(i, std::to_string(i)); } - REQUIRE(map.size() == 100); - for (int i = 0; i < 100; ++i) { + REQUIRE(map.size() == total); + for (int i = 0; i < total; ++i) { auto it = map.find(i); REQUIRE(it != map.end()); REQUIRE(it->first == i); REQUIRE(it->second == std::to_string(i)); } - map.erase(123); - REQUIRE(map.size() == 100); + map.erase(total + 123); + REQUIRE(map.size() == total); map.erase(29); - REQUIRE(map.size() == 99); + REQUIRE(map.size() == total - 1); - map.emplace(std::pair(9999, "hello")); - REQUIRE(map.size() == 100); + map.emplace(std::pair(9999999, "hello")); + REQUIRE(map.size() == total); } -#endif \ No newline at end of file + +#endif // ANKERL_UNORDERED_DENSE_HAS_BOOST diff --git a/test/unit/custom_hash.cpp b/test/unit/custom_hash.cpp index 0d3c7f06..0ef1860d 100644 --- a/test/unit/custom_hash.cpp +++ b/test/unit/custom_hash.cpp @@ -1,12 +1,13 @@ #include -#include +#include + #include namespace { struct id { - uint64_t value{}; + uint64_t value{}; // NOLINT auto operator==(id const& other) const -> bool { return value == other.value; @@ -28,8 +29,8 @@ struct custom_hash_avalanching { }; struct point { - int x{}; - int y{}; + int x{}; // NOLINT + int y{}; // NOLINT auto operator==(point const& other) const -> bool { return x == other.x && y == other.y; @@ -56,23 +57,29 @@ struct ankerl::unordered_dense::hash { } }; -TEST_CASE("custom_hash") { - { - auto set = ankerl::unordered_dense::set(); - set.insert(id{124}); - } - { - auto set = ankerl::unordered_dense::set(); - set.insert(id{124}); - } - { - auto set = ankerl::unordered_dense::set(); - set.insert(point{123, 321}); - } - { - auto set = ankerl::unordered_dense::set(); - set.insert(id{124}); - } +TYPE_TO_STRING_SET(id); +TYPE_TO_STRING_SET(id, custom_hash_simple); +TYPE_TO_STRING_SET(id, custom_hash_avalanching); +TYPE_TO_STRING_SET(point, custom_hash_unique_object_representation); + +TEST_CASE_SET("custom_hash_simple", id, custom_hash_simple) { + auto set = set_t(); + set.insert(id{124}); +} + +TEST_CASE_SET("custom_hash_avalanching", id, custom_hash_avalanching) { + auto set = set_t(); + set.insert(id{124}); +} + +TEST_CASE_SET("custom_hash_unique", point, custom_hash_unique_object_representation) { + auto set = set_t(); + set.insert(point{123, 321}); +} + +TEST_CASE_SET("custom_hash_default", id) { + auto set = set_t(); + set.insert(id{124}); } static_assert( diff --git a/test/unit/diamond.cpp b/test/unit/diamond.cpp index 3b67ecee..b3e07e0a 100644 --- a/test/unit/diamond.cpp +++ b/test/unit/diamond.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include // for size_t #include // for vector @@ -16,9 +16,11 @@ struct hash_with_equal { } }; +TYPE_TO_STRING_MAP(int, int, hash_with_equal, hash_with_equal); + // make sure the map works with the same type (check that it handles the diamond problem) -TEST_CASE("diamond_problem") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("diamond_problem", int, int, hash_with_equal, hash_with_equal) { + auto map = map_t(); map[1] = 2; REQUIRE(map.size() == 1); REQUIRE(map.find(1) != map.end()); diff --git a/test/unit/empty.cpp b/test/unit/empty.cpp index 000e10cb..db2fa0f4 100644 --- a/test/unit/empty.cpp +++ b/test/unit/empty.cpp @@ -1,10 +1,8 @@ #include -#include +#include -using map_t = ankerl::unordered_dense::map; - -TEST_CASE("empty_map_operations") { +TEST_CASE_MAP("empty_map_operations", int, int) { map_t m; REQUIRE(m.end() == m.find(123)); diff --git a/test/unit/equal_range.cpp b/test/unit/equal_range.cpp index 4a773153..958dfe14 100644 --- a/test/unit/equal_range.cpp +++ b/test/unit/equal_range.cpp @@ -1,13 +1,13 @@ #include -#include +#include #include // for add_const_t #include // for pair, as_const #include // for vector -TEST_CASE("equal_range") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("equal_range", int, int) { + auto map = map_t(); // auto map = std::unordered_map(); auto range = map.equal_range(123); diff --git a/test/unit/erase.cpp b/test/unit/erase.cpp index 4a5bbf97..75b38839 100644 --- a/test/unit/erase.cpp +++ b/test/unit/erase.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include // for all_of #include // for size_t @@ -20,8 +20,8 @@ template }); } -TEST_CASE("insert_erase_random") { - auto uds = ankerl::unordered_dense::set(); +TEST_CASE_SET("insert_erase_random", uint32_t) { + auto uds = set_t(); auto us = std::unordered_set(); auto rng = ankerl::nanobench::Rng(123); for (size_t i = 0; i < 10000; ++i) { @@ -42,7 +42,7 @@ TEST_CASE("insert_erase_random") { REQUIRE(is_eq(uds, us)); } -TEST_CASE("erase") { +TEST_CASE_MAP("erase", int, int) { auto map = ankerl::unordered_dense::map(); REQUIRE(0 == map.erase(123)); REQUIRE(0 == map.count(0)); diff --git a/test/unit/erase_if.cpp b/test/unit/erase_if.cpp index e0a63ca5..2005cbbe 100644 --- a/test/unit/erase_if.cpp +++ b/test/unit/erase_if.cpp @@ -1,16 +1,13 @@ #include #include - -#include +#include #include // for size_t #include // for uint64_t #include // for pair -using set_t = ankerl::unordered_dense::set; - -TEST_CASE("erase_if_set") { +TEST_CASE_SET("erase_if_set", counter::obj) { auto counts = counter(); INFO(counts); auto set = set_t(); @@ -33,8 +30,8 @@ TEST_CASE("erase_if_set") { } } -TEST_CASE("erase_if_map") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("erase_if_map", uint64_t, uint64_t) { + auto map = map_t(); for (size_t i = 0; i < 1000; ++i) { map.try_emplace(i, i); } diff --git a/test/unit/erase_range.cpp b/test/unit/erase_range.cpp index c7d8406f..8f80b6fb 100644 --- a/test/unit/erase_range.cpp +++ b/test/unit/erase_range.cpp @@ -1,14 +1,13 @@ #include #include +#include #include -#include - #include // for size_t #include // for vector -TEST_CASE("erase_range") { +TEST_CASE_MAP("erase_range", counter::obj, counter::obj) { int const num_elements = 10; for (int first_pos = 0; first_pos <= num_elements; ++first_pos) { @@ -16,7 +15,7 @@ TEST_CASE("erase_range") { auto counts = counter(); INFO(counts); - auto map = ankerl::unordered_dense::map(); + auto map = map_t(); for (size_t i = 0; i < num_elements; ++i) { auto key = i; diff --git a/test/unit/explicit.cpp b/test/unit/explicit.cpp index 19dcf3b6..f89c5eee 100644 --- a/test/unit/explicit.cpp +++ b/test/unit/explicit.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -15,15 +15,19 @@ struct per_image { std::unordered_map m_texture_index; }; +template struct scene { std::vector m_per_image; - ankerl::unordered_dense::map m_textures_per_key; + Map m_textures_per_key; }; +template struct app_state { - scene m_scene; + scene m_scene; }; -TEST_CASE("unit_create_AppState_issue_97") { - app_state app_state{}; +TYPE_TO_STRING_MAP(void*, texture*); + +TEST_CASE_MAP("unit_create_AppState_issue_97", void*, texture*) { + app_state const app_state{}; } diff --git a/test/unit/extract.cpp b/test/unit/extract.cpp index 13417b18..75b7ac04 100644 --- a/test/unit/extract.cpp +++ b/test/unit/extract.cpp @@ -1,15 +1,15 @@ #include -#include -#include +#include +#include -TEST_CASE("extract") { +TEST_CASE_MAP("extract", counter::obj, counter::obj) { auto counts = counter(); INFO(counts); - auto container = std::vector>(); + auto container = typename map_t::value_container_type(); { - auto map = ankerl::unordered_dense::map(); + auto map = map_t(); for (size_t i = 0; i < 100; ++i) { map.try_emplace(counter::obj{i, counts}, i, counts); diff --git a/test/unit/fuzz_corpus.cpp b/test/unit/fuzz_corpus.cpp index d0efff2b..12e903f7 100644 --- a/test/unit/fuzz_corpus.cpp +++ b/test/unit/fuzz_corpus.cpp @@ -78,18 +78,42 @@ void run_corpus(std::string_view name, Op op) { } // namespace -TEST_CASE("fuzz_api" * doctest::test_suite("fuzz") * doctest::skip(true)) { - run_corpus("api", fuzz::api); +TEST_CASE("fuzz_api_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("api", fuzz::api_map); +} +TEST_CASE("fuzz_api_segmented_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("api", fuzz::api_segmented_map); +} +TEST_CASE("fuzz_api_deque_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("api", fuzz::api_deque_map); } -TEST_CASE("fuzz_replace" * doctest::test_suite("fuzz") * doctest::skip(true)) { - run_corpus("replace", fuzz::replace); +TEST_CASE("fuzz_replace_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("replace", fuzz::replace_map); +} +TEST_CASE("fuzz_replace_segmented_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("replace", fuzz::replace_segmented_map); +} +TEST_CASE("fuzz_replace_deque_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("replace", fuzz::replace_deque_map); } -TEST_CASE("fuzz_insert_erase" * doctest::test_suite("fuzz") * doctest::skip(true)) { - run_corpus("insert_erase", fuzz::insert_erase); +TEST_CASE("fuzz_insert_erase_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("insert_erase", fuzz::insert_erase_map); +} +TEST_CASE("fuzz_insert_erase_segmented_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("insert_erase", fuzz::insert_erase_segmented_map); +} +TEST_CASE("fuzz_insert_erase_deque_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("insert_erase", fuzz::insert_erase_deque_map); } -TEST_CASE("fuzz_string" * doctest::test_suite("fuzz") * doctest::skip(true)) { - run_corpus("string", fuzz::string); +TEST_CASE("fuzz_string_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("string", fuzz::string_map); +} +TEST_CASE("fuzz_string_segmented_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("string", fuzz::string_segmented_map); +} +TEST_CASE("fuzz_string_deque_map" * doctest::test_suite("fuzz") * doctest::skip(true)) { + run_corpus("string", fuzz::string_deque_map); } diff --git a/test/unit/initializer_list.cpp b/test/unit/initializer_list.cpp index 04391df2..4685462d 100644 --- a/test/unit/initializer_list.cpp +++ b/test/unit/initializer_list.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include // for size_t #include // for string, operator==, allocator @@ -16,8 +16,8 @@ auto has(Map const& map, First const& first, Second const& second) -> bool { return it != map.end() && it->second == second; } -TEST_CASE("insert_initializer_list") { - auto m = ankerl::unordered_dense::map(); +TEST_CASE_MAP("insert_initializer_list", int, int) { + auto m = map_t(); m.insert({{1, 2}, {3, 4}, {5, 6}}); REQUIRE(m.size() == 3U); REQUIRE(m[1] == 2); @@ -25,12 +25,12 @@ TEST_CASE("insert_initializer_list") { REQUIRE(m[5] == 6); } -TEST_CASE("initializerlist_string") { +TEST_CASE_MAP("initializerlist_string", std::string, size_t) { size_t const n1 = 17; size_t const n2 = 10; - auto m1 = ankerl::unordered_dense::map{{"duck", n1}, {"lion", n2}}; - auto m2 = ankerl::unordered_dense::map{{"duck", n1}, {"lion", n2}}; + auto m1 = map_t{{"duck", n1}, {"lion", n2}}; + auto m2 = map_t{{"duck", n1}, {"lion", n2}}; REQUIRE(m1.size() == 2); REQUIRE(m1["duck"] == n1); @@ -42,8 +42,8 @@ TEST_CASE("initializerlist_string") { REQUIRE(m2["lion"] == n2); } -TEST_CASE("insert_initializer_list_string") { - auto m = ankerl::unordered_dense::map(); +TEST_CASE_MAP("insert_initializer_list_string", int, std::string) { + auto m = map_t(); m.insert({{1, "a"}, {3, "b"}, {5, "c"}}); REQUIRE(m.size() == 3U); REQUIRE(m[1] == "a"); @@ -51,8 +51,8 @@ TEST_CASE("insert_initializer_list_string") { REQUIRE(m[5] == "c"); } -TEST_CASE("initializer_list_assign") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("initializer_list_assign", int, char const*) { + auto map = map_t(); map[3] = "nope"; map = {{1, "a"}, {2, "hello"}, {12346, "world!"}}; REQUIRE(map.size() == 3); @@ -61,20 +61,19 @@ TEST_CASE("initializer_list_assign") { REQUIRE(has(map, 12346, "world!"sv)); } -TEST_CASE("initializer_list_ctor_alloc") { +TEST_CASE_MAP("initializer_list_ctor_alloc", int, char const*) { using alloc_t = std::allocator>; - auto map = ankerl::unordered_dense::map({{1, "a"}, {2, "hello"}, {12346, "world!"}}, 0, alloc_t{}); + auto map = map_t({{1, "a"}, {2, "hello"}, {12346, "world!"}}, 0, alloc_t{}); REQUIRE(map.size() == 3); REQUIRE(has(map, 1, "a"sv)); REQUIRE(has(map, 2, "hello"sv)); REQUIRE(has(map, 12346, "world!"sv)); } -TEST_CASE("initializer_list_ctor_hash_alloc") { +TEST_CASE_MAP("initializer_list_ctor_hash_alloc", int, char const*) { using hash_t = ankerl::unordered_dense::hash; using alloc_t = std::allocator>; - auto map = - ankerl::unordered_dense::map({{1, "a"}, {2, "hello"}, {12346, "world!"}}, 0, hash_t{}, alloc_t{}); + auto map = map_t({{1, "a"}, {2, "hello"}, {12346, "world!"}}, 0, hash_t{}, alloc_t{}); REQUIRE(map.size() == 3); REQUIRE(has(map, 1, "a"sv)); REQUIRE(has(map, 2, "hello"sv)); diff --git a/test/unit/insert.cpp b/test/unit/insert.cpp index 0397e7f1..ad194426 100644 --- a/test/unit/insert.cpp +++ b/test/unit/insert.cpp @@ -1,32 +1,31 @@ #include -#include +#include #include // for forward_as_tuple #include // for piecewise_construct #include // for vector -TEST_CASE("insert") { - using map_t = ankerl::unordered_dense::map; +TEST_CASE_MAP("insert", unsigned int, int) { auto map = map_t(); - typename map_t::value_type val(123U, 321); + auto const val = typename map_t::value_type(123U, 321); map.insert(val); REQUIRE(map.size() == 1); REQUIRE(map[123U] == 321); } -TEST_CASE("insert_hint") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("insert_hint", unsigned int, int) { + auto map = map_t(); auto it = map.insert(map.begin(), {1, 2}); - auto vt = decltype(map)::value_type{3, 4}; + auto vt = typename decltype(map)::value_type{3, 4}; map.insert(it, vt); REQUIRE(map.size() == 2); REQUIRE(map[1] == 2); REQUIRE(map[3] == 4); - auto const vt2 = decltype(map)::value_type{10, 11}; + auto const vt2 = typename decltype(map)::value_type{10, 11}; map.insert(it, vt2); REQUIRE(map.size() == 3); REQUIRE(map[10] == 11); diff --git a/test/unit/insert_or_assign.cpp b/test/unit/insert_or_assign.cpp index f85beab5..73a9ea8c 100644 --- a/test/unit/insert_or_assign.cpp +++ b/test/unit/insert_or_assign.cpp @@ -1,13 +1,13 @@ #include -#include +#include #include // for string, operator==, allocator #include // for pair #include // for vector -TEST_CASE("insert_or_assign") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("insert_or_assign", std::string, std::string) { + auto map = map_t(); auto ret = map.insert_or_assign("a", "b"); REQUIRE(ret.second); diff --git a/test/unit/iterators_empty.cpp b/test/unit/iterators_empty.cpp index e2f3325e..bad7a336 100644 --- a/test/unit/iterators_empty.cpp +++ b/test/unit/iterators_empty.cpp @@ -1,14 +1,14 @@ #include -#include +#include #include // for size_t #include // for uint64_t #include // for vector -TEST_CASE("iterators_empty") { +TEST_CASE_MAP("iterators_empty", size_t, size_t) { for (size_t i = 0; i < 10; ++i) { - auto m = ankerl::unordered_dense::map(); + auto m = ankerl::unordered_dense::map(); REQUIRE(m.begin() == m.end()); REQUIRE(m.end() == m.find(132)); diff --git a/test/unit/iterators_erase.cpp b/test/unit/iterators_erase.cpp index 95a85114..49748e13 100644 --- a/test/unit/iterators_erase.cpp +++ b/test/unit/iterators_erase.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include // for size_t #include // for uint64_t @@ -9,12 +9,12 @@ #include // for pair #include // for vector -TEST_CASE("iterators_erase") { +TEST_CASE_MAP("iterators_erase", counter::obj, counter::obj) { auto counts = counter(); INFO(counts); { counts("begin"); - auto map = ankerl::unordered_dense::map(); + auto map = map_t(); for (size_t i = 0; i < 100; ++i) { map[counter::obj(i * 101, counts)] = counter::obj(i * 101, counts); } @@ -27,13 +27,13 @@ TEST_CASE("iterators_erase") { REQUIRE(map.end() == map.find(counter::obj(size_t{20} * 101, counts))); it = map.begin(); - size_t currentSize = map.size(); + size_t current_size = map.size(); std::unordered_set keys; while (it != map.end()) { REQUIRE(keys.emplace(it->first.get()).second); it = map.erase(it); - currentSize--; - REQUIRE(map.size() == currentSize); + current_size--; + REQUIRE(map.size() == current_size); } REQUIRE(map.size() == static_cast(0)); counts("done"); diff --git a/test/unit/iterators_insert.cpp b/test/unit/iterators_insert.cpp index e9a14514..01bd2e93 100644 --- a/test/unit/iterators_insert.cpp +++ b/test/unit/iterators_insert.cpp @@ -1,19 +1,19 @@ #include -#include +#include #include // for max #include // for pair #include // for vector -TEST_CASE("iterators_insert") { +TEST_CASE_MAP("iterators_insert", int, int) { auto v = std::vector>(); v.reserve(1000); for (int i = 0; i < 1000; ++i) { v.emplace_back(i, i); } - auto map = ankerl::unordered_dense::map(v.begin(), v.end()); + auto map = map_t(v.begin(), v.end()); REQUIRE(map.size() == v.size()); for (auto const& kv : v) { REQUIRE(map.count(kv.first) == 1); diff --git a/test/unit/load_factor.cpp b/test/unit/load_factor.cpp index dc4f3948..e6828170 100644 --- a/test/unit/load_factor.cpp +++ b/test/unit/load_factor.cpp @@ -1,9 +1,9 @@ #include -#include +#include -TEST_CASE("load_factor") { - auto m = ankerl::unordered_dense::map(); +TEST_CASE_MAP("load_factor", int, int) { + auto m = map_t(); REQUIRE(static_cast(m.load_factor()) == doctest::Approx(0.0)); diff --git a/test/unit/maps_of_maps.cpp b/test/unit/maps_of_maps.cpp index 6835b93f..f7d19cd5 100644 --- a/test/unit/maps_of_maps.cpp +++ b/test/unit/maps_of_maps.cpp @@ -2,25 +2,24 @@ #include #include -#include +#include -#include +#include #include // for size_t #include // for uint64_t #include // for move -TEST_CASE("mapmap") { +template +void test() { auto counts = counter(); INFO(counts); - using map_t = ankerl::unordered_dense::map>; - auto rng = ankerl::nanobench::Rng(); for (size_t trial = 0; trial < 4; ++trial) { { counts("start"); - auto maps = map_t(); + auto maps = Map(); for (size_t i = 0; i < 100; ++i) { auto a = rng.bounded(20); auto b = rng.bounded(20); @@ -30,25 +29,25 @@ TEST_CASE("mapmap") { } counts("filled"); - map_t mapsCopied; - mapsCopied = maps; - REQUIRE(checksum::mapmap(mapsCopied) == checksum::mapmap(maps)); - REQUIRE(mapsCopied == maps); + Map maps_copied; + maps_copied = maps; + REQUIRE(checksum::mapmap(maps_copied) == checksum::mapmap(maps)); + REQUIRE(maps_copied == maps); counts("copied"); - map_t mapsMoved; - mapsMoved = std::move(mapsCopied); + Map maps_moved; + maps_moved = std::move(maps_copied); counts("moved"); // move - REQUIRE(checksum::mapmap(mapsMoved) == checksum::mapmap(maps)); - REQUIRE(mapsCopied.size() == 0); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved) - mapsCopied = std::move(mapsMoved); + REQUIRE(checksum::mapmap(maps_moved) == checksum::mapmap(maps)); + REQUIRE(maps_copied.size() == 0); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved) + maps_copied = std::move(maps_moved); counts("moved back"); // move back - REQUIRE(checksum::mapmap(mapsCopied) == checksum::mapmap(maps)); - REQUIRE(mapsMoved.size() == 0); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved) + REQUIRE(checksum::mapmap(maps_copied) == checksum::mapmap(maps)); + REQUIRE(maps_moved.size() == 0); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved) counts("done"); } counts("all destructed"); @@ -56,3 +55,14 @@ TEST_CASE("mapmap") { counts.ctor() + counts.static_default_ctor + counts.copy_ctor() + counts.default_ctor() + counts.move_ctor()); } } + +TYPE_TO_STRING_MAP(counter::obj, ankerl::unordered_dense::map); +TYPE_TO_STRING_MAP(counter::obj, ankerl::unordered_dense::segmented_map); + +TEST_CASE_MAP("mapmap_map", counter::obj, ankerl::unordered_dense::map) { + test(); +} + +TEST_CASE_MAP("mapmap_segmented_map", counter::obj, ankerl::unordered_dense::segmented_map) { + test(); +} diff --git a/test/unit/max.cpp b/test/unit/max.cpp index 7e654481..b18744df 100644 --- a/test/unit/max.cpp +++ b/test/unit/max.cpp @@ -1,14 +1,14 @@ #include -#include +#include #include // for uint32_t #include // for equal_to #include // for numeric_limits -TEST_CASE("max_load_factor") { - auto map_60 = ankerl::unordered_dense::map(); - auto map_90 = ankerl::unordered_dense::map(); +TEST_CASE_MAP("max_load_factor", int, int) { + auto map_60 = map_t(); + auto map_90 = map_t(); REQUIRE(map_60.max_load_factor() == doctest::Approx(0.8)); map_60.max_load_factor(0.6F); map_90.max_load_factor(0.9F); @@ -37,8 +37,8 @@ TEST_CASE("max_load_factor") { REQUIRE(map_60.max_load_factor() == map_90.max_load_factor()); } -TEST_CASE("key_eq") { - auto const map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("key_eq", int, int) { + auto const map = map_t(); auto eq = map.key_eq(); REQUIRE(eq(5, 5)); } diff --git a/test/unit/move_to_moved.cpp b/test/unit/move_to_moved.cpp index 9ce70522..efa35130 100644 --- a/test/unit/move_to_moved.cpp +++ b/test/unit/move_to_moved.cpp @@ -1,19 +1,18 @@ #include #define ENABLE_LOG_LINE +#include #include -#include - #include // for remove_reference, remove_referen... #include // for move -TEST_CASE("move_to_moved") { - auto a = ankerl::unordered_dense::map(); +TEST_CASE_MAP("move_to_moved", int, int) { + auto a = map_t(); a[1] = 2; auto moved = std::move(a); - auto c = ankerl::unordered_dense::map(); + auto c = map_t(); c[3] = 4; // assign to a moved map diff --git a/test/unit/multiple_apis.cpp b/test/unit/multiple_apis.cpp index 49b7ee53..63972391 100644 --- a/test/unit/multiple_apis.cpp +++ b/test/unit/multiple_apis.cpp @@ -1,10 +1,9 @@ #include #include +#include #include -#include - #include // for size_t #include // for uint64_t #include // for __enable_if_t @@ -12,8 +11,7 @@ #include // for pair, make_pair #include // for vector -TEST_CASE("multiple_different_APIs" * doctest::test_suite("stochastic")) { - using map_t = ankerl::unordered_dense::map; +TEST_CASE_MAP("multiple_different_APIs" * doctest::test_suite("stochastic"), counter::obj, counter::obj) { counter counts; INFO(counts); @@ -73,12 +71,12 @@ TEST_CASE("multiple_different_APIs" * doctest::test_suite("stochastic")) { REQUIRE(map.size() == uo.size()); r = rng.bounded(times / 4); - auto mapIt = map.find(counter::obj{r, counts}); - auto uoIt = uo.find(r); - REQUIRE((map.end() == mapIt) == (uo.end() == uoIt)); - if (map.end() != mapIt) { - REQUIRE(mapIt->first.get() == uoIt->first); - REQUIRE(mapIt->second.get() == uoIt->second); + auto map_it = map.find(counter::obj{r, counts}); + auto uo_it2 = uo.find(r); + REQUIRE((map.end() == map_it) == (uo.end() == uo_it2)); + if (map.end() != map_it) { + REQUIRE(map_it->first.get() == uo_it2->first); + REQUIRE(map_it->second.get() == uo_it2->second); } } @@ -92,18 +90,18 @@ TEST_CASE("multiple_different_APIs" * doctest::test_suite("stochastic")) { REQUIRE(map.size() == uo.size()); } - std::size_t numChecks = 0; + std::size_t num_checks = 0; for (auto it = map.begin(); it != map.end(); ++it) { REQUIRE(uo.end() != uo.find(it->first.get())); - ++numChecks; + ++num_checks; } - REQUIRE(map.size() == numChecks); + REQUIRE(map.size() == num_checks); - numChecks = 0; + num_checks = 0; map_t const& const_rhhs = map; for (const typename map_t::value_type& vt : const_rhhs) { REQUIRE(uo.end() != uo.find(vt.first.get())); - ++numChecks; + ++num_checks; } - REQUIRE(map.size() == numChecks); + REQUIRE(map.size() == num_checks); } diff --git a/test/unit/namespace.cpp b/test/unit/namespace.cpp index 6f2a9668..a2968d66 100644 --- a/test/unit/namespace.cpp +++ b/test/unit/namespace.cpp @@ -2,7 +2,7 @@ #include -namespace versioned_namespace = ankerl::unordered_dense::v3_1_1; +namespace versioned_namespace = ankerl::unordered_dense::v4_0_0; static_assert(std::is_same_v, ankerl::unordered_dense::map>); static_assert(std::is_same_v, ankerl::unordered_dense::hash>); diff --git a/test/unit/not_copyable.cpp b/test/unit/not_copyable.cpp index 70d5c376..acd4f398 100644 --- a/test/unit/not_copyable.cpp +++ b/test/unit/not_copyable.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include // for size_t #include // for forward_as_tuple @@ -28,9 +28,9 @@ class no_copy { size_t m_mData{}; }; -TEST_CASE("not_copyable") { - using map_t = ankerl::unordered_dense::map; +TYPE_TO_STRING_MAP(size_t, no_copy); +TEST_CASE_MAP("not_copyable", size_t, no_copy) { // it's ok because it is movable. map_t m; for (size_t i = 0; i < 100; ++i) { diff --git a/test/unit/not_moveable.cpp b/test/unit/not_moveable.cpp index 791a8573..be6460e3 100644 --- a/test/unit/not_moveable.cpp +++ b/test/unit/not_moveable.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include // for size_t #include // for forward_as_tuple @@ -27,11 +27,11 @@ class no_move { size_t m_mData{}; }; +TYPE_TO_STRING_MAP(size_t, no_move); + // doesn't work with robin_hood::unordered_flat_map because not movable and not // copyable -TEST_CASE("not_moveable") { - using map_t = ankerl::unordered_dense::map; - +TEST_CASE_MAP("not_moveable", size_t, no_move) { // it's ok because it is movable. auto m = map_t(); for (size_t i = 0; i < 100; ++i) { diff --git a/test/unit/pmr.cpp b/test/unit/pmr.cpp index b26b4cb4..cf3ced10 100644 --- a/test/unit/pmr.cpp +++ b/test/unit/pmr.cpp @@ -1,6 +1,7 @@ #include -#include +#include + #include #include // for size_t @@ -10,48 +11,48 @@ #include // for move #include // for vector -#if ANKERL_UNORDERED_DENSE_PMR +#if defined(ANKERL_UNORDERED_DENSE_PMR) -# if __has_include() -# include +// windows' vector has different allocation behavior, macos has linker errors +# if __linux__ -class logging_memory_resource : public std::pmr::memory_resource { +class logging_memory_resource : public ANKERL_UNORDERED_DENSE_PMR::memory_resource { auto do_allocate(std::size_t bytes, std::size_t alignment) -> void* override { fmt::print("+ {} bytes, {} alignment\n", bytes, alignment); - return std::pmr::new_delete_resource()->allocate(bytes, alignment); + return ANKERL_UNORDERED_DENSE_PMR::new_delete_resource()->allocate(bytes, alignment); } void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override { fmt::print("- {} bytes, {} alignment, {} ptr\n", bytes, alignment, p); - return std::pmr::new_delete_resource()->deallocate(p, bytes, alignment); + return ANKERL_UNORDERED_DENSE_PMR::new_delete_resource()->deallocate(p, bytes, alignment); } - [[nodiscard]] auto do_is_equal(const std::pmr::memory_resource& other) const noexcept -> bool override { + [[nodiscard]] auto do_is_equal(const ANKERL_UNORDERED_DENSE_PMR::memory_resource& other) const noexcept -> bool override { return this == &other; } }; -class no_null_memory_resource : public std::pmr::memory_resource { +class no_null_memory_resource : public ANKERL_UNORDERED_DENSE_PMR::memory_resource { auto do_allocate(std::size_t bytes, std::size_t alignment) -> void* override { if (bytes == 0) { throw std::runtime_error("no_null_memory_resource::do_allocate should not do_allocate 0"); } - return std::pmr::new_delete_resource()->allocate(bytes, alignment); + return ANKERL_UNORDERED_DENSE_PMR::new_delete_resource()->allocate(bytes, alignment); } void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override { if (nullptr == p || 0U == bytes || 0U == alignment) { throw std::runtime_error("no_null_memory_resource::do_deallocate should not deallocate with any 0 value"); } - return std::pmr::new_delete_resource()->deallocate(p, bytes, alignment); + return ANKERL_UNORDERED_DENSE_PMR::new_delete_resource()->deallocate(p, bytes, alignment); } - [[nodiscard]] auto do_is_equal(const std::pmr::memory_resource& other) const noexcept -> bool override { + [[nodiscard]] auto do_is_equal(const ANKERL_UNORDERED_DENSE_PMR::memory_resource& other) const noexcept -> bool override { return this == &other; } }; -class track_peak_memory_resource : public std::pmr::memory_resource { +class track_peak_memory_resource : public ANKERL_UNORDERED_DENSE_PMR::memory_resource { uint64_t m_peak = 0; uint64_t m_current = 0; uint64_t m_num_allocs = 0; @@ -64,7 +65,7 @@ class track_peak_memory_resource : public std::pmr::memory_resource { if (m_current > m_peak) { m_peak = m_current; } - return std::pmr::new_delete_resource()->allocate(bytes, alignment); + return ANKERL_UNORDERED_DENSE_PMR::new_delete_resource()->allocate(bytes, alignment); } void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override { @@ -73,10 +74,10 @@ class track_peak_memory_resource : public std::pmr::memory_resource { } ++m_num_deallocs; m_current -= bytes; - return std::pmr::new_delete_resource()->deallocate(p, bytes, alignment); + return ANKERL_UNORDERED_DENSE_PMR::new_delete_resource()->deallocate(p, bytes, alignment); } - [[nodiscard]] auto do_is_equal(const std::pmr::memory_resource& other) const noexcept -> bool override { + [[nodiscard]] auto do_is_equal(const ANKERL_UNORDERED_DENSE_PMR::memory_resource& other) const noexcept -> bool override { ++m_num_is_equals; return this == &other; } @@ -103,11 +104,13 @@ class track_peak_memory_resource : public std::pmr::memory_resource { } }; -TEST_CASE("pmr") { +TYPE_TO_STRING_PMR_MAP(uint64_t, uint64_t); + +TEST_CASE_PMR_MAP("pmr", uint64_t, uint64_t) { auto mr = track_peak_memory_resource(); { REQUIRE(mr.current() == 0); - auto map = ankerl::unordered_dense::pmr::map(&mr); + auto map = map_t(&mr); for (size_t i = 0; i < 1; ++i) { map[i] = i; @@ -121,18 +124,18 @@ TEST_CASE("pmr") { REQUIRE(mr.current() == 0); } -TEST_CASE("pmr_no_null") { +TEST_CASE_PMR_MAP("pmr_no_null", uint64_t, uint64_t) { auto mr = no_null_memory_resource(); - { auto map = ankerl::unordered_dense::pmr::map(&mr); } + { auto map = map_t(&mr); } { - auto map = ankerl::unordered_dense::pmr::map(&mr); + auto map = map_t(&mr); for (size_t i = 0; i < 1; ++i) { map[i] = i; } } { - auto map = ankerl::unordered_dense::pmr::map(&mr); + auto map = map_t(&mr); for (size_t i = 0; i < 1; ++i) { map[i] = i; } @@ -144,15 +147,16 @@ void show([[maybe_unused]] track_peak_memory_resource const& mr, [[maybe_unused] // fmt::print("{}: {} allocs, {} deallocs, {} is_equals\n", name, mr.num_allocs(), mr.num_deallocs(), mr.num_is_equals()); } -// windows' vector has different allocation behavior -# ifndef _WIN32 +// the following tests are vector specific, so don't use segmented_vector + TEST_CASE("pmr_copy") { + using map_t = ankerl::unordered_dense::pmr::map; auto mr1 = track_peak_memory_resource(); - auto map1 = ankerl::unordered_dense::pmr::map(&mr1); + auto map1 = map_t(&mr1); map1[1] = 2; auto mr2 = track_peak_memory_resource(); - auto map2 = ankerl::unordered_dense::pmr::map(&mr2); + auto map2 = map_t(&mr2); map2[3] = 4; show(mr1, "mr1"); show(mr2, "mr2"); @@ -173,12 +177,14 @@ TEST_CASE("pmr_copy") { } TEST_CASE("pmr_move_different_mr") { + using map_t = ankerl::unordered_dense::pmr::map; + auto mr1 = track_peak_memory_resource(); - auto map1 = ankerl::unordered_dense::pmr::map(&mr1); + auto map1 = map_t(&mr1); map1[1] = 2; auto mr2 = track_peak_memory_resource(); - auto map2 = ankerl::unordered_dense::pmr::map(&mr2); + auto map2 = map_t(&mr2); map2[3] = 4; show(mr1, "mr1"); show(mr2, "mr2"); @@ -199,8 +205,10 @@ TEST_CASE("pmr_move_different_mr") { } TEST_CASE("pmr_move_same_mr") { + using map_t = ankerl::unordered_dense::pmr::map; + auto mr1 = track_peak_memory_resource(); - auto map1 = ankerl::unordered_dense::pmr::map(&mr1); + auto map1 = map_t(&mr1); map1[1] = 2; REQUIRE(mr1.num_allocs() == 2); REQUIRE(mr1.num_deallocs() == 0); @@ -219,7 +227,6 @@ TEST_CASE("pmr_move_same_mr") { REQUIRE(mr1.num_deallocs() == 2); REQUIRE(mr1.num_is_equals() == 0); } -# endif # endif -#endif +#endif \ No newline at end of file diff --git a/test/unit/rehash.cpp b/test/unit/rehash.cpp index 0190ce1c..b12aeace 100644 --- a/test/unit/rehash.cpp +++ b/test/unit/rehash.cpp @@ -1,11 +1,11 @@ #include -#include +#include #include // for size_t -TEST_CASE("rehash") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("rehash", size_t, int) { + auto map = map_t(); for (size_t i = 0; i < 1000; ++i) { map[i]; diff --git a/test/unit/replace.cpp b/test/unit/replace.cpp index 384d5f39..fd0fa343 100644 --- a/test/unit/replace.cpp +++ b/test/unit/replace.cpp @@ -1,15 +1,13 @@ #include #include +#include -#include - -TEST_CASE("replace") { +TEST_CASE_MAP("replace", counter::obj, counter::obj) { auto counts = counter{}; INFO(counts); - using map_t = ankerl::unordered_dense::map; - auto container = map_t::value_container_type{}; + auto container = typename map_t::value_container_type{}; for (size_t i = 0; i < 100; ++i) { container.emplace_back(counter::obj{i, counts}, counter::obj{i, counts}); diff --git a/test/unit/reserve.cpp b/test/unit/reserve.cpp index 4f629d1a..a236747c 100644 --- a/test/unit/reserve.cpp +++ b/test/unit/reserve.cpp @@ -1,11 +1,14 @@ #include -#include +#include -TEST_CASE("reserve") { - auto map = ankerl::unordered_dense::map(); - REQUIRE(map.values().capacity() <= 1000U); - map.reserve(1000); - REQUIRE(map.values().capacity() >= 1000U); - REQUIRE(0U == map.values().size()); +TEST_CASE_MAP("reserve", int, int) { + // there's no capacity() for std::deque + if constexpr (!std::is_same_v>>) { + auto map = map_t(); + REQUIRE(map.values().capacity() <= 1000U); + map.reserve(1000); + REQUIRE(map.values().capacity() >= 1000U); + REQUIRE(0U == map.values().size()); + } } diff --git a/test/unit/reserve_and_assign.cpp b/test/unit/reserve_and_assign.cpp index d5c14bce..abba6728 100644 --- a/test/unit/reserve_and_assign.cpp +++ b/test/unit/reserve_and_assign.cpp @@ -1,15 +1,13 @@ #include #include - -#include +#include #include // for uint64_t #include // for allocator, string #include // for vector -TEST_CASE("reserve_and_assign") { - using map_t = ankerl::unordered_dense::map; +TEST_CASE_MAP("reserve_and_assign", std::string, uint64_t) { map_t a = { {"button", {}}, {"selectbox-tr", {}}, @@ -84,7 +82,7 @@ TEST_CASE("reserve_and_assign") { REQUIRE(c.find("button") != c.end()); // Fails. } -TEST_CASE("unit_reserve_only_flat") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("unit_reserve_only_flat", std::string, uint64_t) { + auto map = map_t(); map.reserve(51); } diff --git a/test/unit/segmented_vector.cpp b/test/unit/segmented_vector.cpp new file mode 100644 index 00000000..2c36b4ee --- /dev/null +++ b/test/unit/segmented_vector.cpp @@ -0,0 +1,137 @@ +#include +#include +#include + +#include + +#include + +#include + +TEST_CASE("segmented_vector") { + counter counts; + INFO(counts); + { + auto vec = ankerl::unordered_dense::segmented_vector(); + for (size_t i = 0; i < 1000; ++i) { + vec.emplace_back(i, counts); + REQUIRE(i + 1 == counts.ctor()); + } + REQUIRE(0 == counts.move_ctor()); + REQUIRE(0 == counts.move_assign()); + counts("before dtor"); + REQUIRE(counts.data() == counter::data_t{1000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + } + counts.check_all_done(); + REQUIRE(0 == counts.move_ctor()); + counts("done"); + REQUIRE(counts.data() == counter::data_t{1000, 0, 0, 1000, 0, 0, 0, 0, 0, 0, 0, 0, 0}); +} + +TEST_CASE("segmented_vector_capacity") { + counter counts; + INFO(counts); + auto vec = + ankerl::unordered_dense::segmented_vector, sizeof(counter::obj) * 4>(); + REQUIRE(0 == vec.capacity()); + for (size_t i = 0; i < 50; ++i) { + REQUIRE(i == vec.size()); + vec.emplace_back(i, counts); + REQUIRE(i + 1 == vec.size()); + REQUIRE(vec.capacity() >= vec.size()); + REQUIRE(0 == vec.capacity() % 4); + } +} + +TEST_CASE("segmented_vector_idx") { + counter counts; + INFO(counts); + auto vec = + ankerl::unordered_dense::segmented_vector, sizeof(counter::obj) * 4>(); + REQUIRE(0 == vec.capacity()); + for (size_t i = 0; i < 50; ++i) { + vec.emplace_back(i, counts); + } + + for (size_t i = 0; i < vec.size(); ++i) { + REQUIRE(i == vec[i].get()); + } +} + +TEST_CASE("segmented_vector_iterate") { + counter counts; + INFO(counts); + auto vec = + ankerl::unordered_dense::segmented_vector, sizeof(counter::obj) * 4>(); + for (size_t i = 0; i < 50; ++i) { + auto it = vec.begin(); + auto end = vec.end(); + + REQUIRE(std::distance(it, end) == vec.size()); + size_t j = 0; + while (it != end) { + REQUIRE(it->get() == j); + ++it; + ++j; + } + vec.emplace_back(i, counts); + } +} + +TEST_CASE("segmented_vector_reserve") { + auto counts = counts_for_allocator{}; + auto vec = ankerl::unordered_dense::segmented_vector, sizeof(int) * 16>(&counts); + + REQUIRE(0 == vec.capacity()); + REQUIRE(counts.size() < 2); + vec.reserve(1100); + REQUIRE(counts.size() > 63); + counts.reset(); + REQUIRE(counts.size() == 0); + REQUIRE(1104 == vec.capacity()); + + for (size_t i = 0; i < vec.capacity(); ++i) { + vec.emplace_back(0); + } + REQUIRE(counts.size() == 0); + vec.emplace_back(123); + + // 3: 2 for std::vector reallocates, 1 for the new segment + REQUIRE(counts.size() == 3); +} + +using vec_t = ankerl::unordered_dense::segmented_vector; +static_assert(sizeof(vec_t) == sizeof(std::vector) + sizeof(size_t)); + +TEST_CASE("bench_segmented_vector" * doctest::test_suite("bench") * doctest::skip()) { + static constexpr auto num_elements = size_t{21233}; + + using namespace std::literals; + + ankerl::nanobench::Rng rng(123); + + auto sv = ankerl::unordered_dense::segmented_vector(); + for (size_t i = 0; i < num_elements; ++i) { + sv.emplace_back(i); + } + + ankerl::nanobench::Bench().minEpochTime(100ms).batch(sv.size()).run("shuffle stable_vector", [&] { + rng.shuffle(sv); + }); + + auto c = std::deque(); + for (size_t i = 0; i < num_elements; ++i) { + c.push_back(i); + } + ankerl::nanobench::Bench().minEpochTime(100ms).batch(sv.size()).run("shuffle std::deque", [&] { + rng.shuffle(c); + }); + + auto v = std::vector(); + for (size_t i = 0; i < num_elements; ++i) { + v.push_back(i); + } + ankerl::nanobench::Bench().minEpochTime(100ms).batch(sv.size()).run("shuffle std::vector", [&] { + rng.shuffle(v); + }); +} \ No newline at end of file diff --git a/test/unit/set.cpp b/test/unit/set.cpp index f177c8c3..2d936dd4 100644 --- a/test/unit/set.cpp +++ b/test/unit/set.cpp @@ -1,12 +1,12 @@ #include -#include +#include #include // for operator==, basic_string, operat... #include // for vector -TEST_CASE("set") { - auto set = ankerl::unordered_dense::set(); +TEST_CASE_SET("set", std::string) { + auto set = set_t(); set.insert("asdf"); REQUIRE(set.size() == 1); diff --git a/test/unit/set_or_map_types.cpp b/test/unit/set_or_map_types.cpp index b7cddb9c..0466cb21 100644 --- a/test/unit/set_or_map_types.cpp +++ b/test/unit/set_or_map_types.cpp @@ -3,9 +3,16 @@ template using detect_has_mapped_type = typename T::mapped_type; -using map_t = ankerl::unordered_dense::map; -static_assert(std::is_same_v); -static_assert(ankerl::unordered_dense::detail::is_detected_v); +using map1_t = ankerl::unordered_dense::map; +static_assert(std::is_same_v); +static_assert(ankerl::unordered_dense::detail::is_detected_v); -using set_t = ankerl::unordered_dense::set; -static_assert(!ankerl::unordered_dense::detail::is_detected_v); +using map2_t = ankerl::unordered_dense::segmented_map; +static_assert(std::is_same_v); +static_assert(ankerl::unordered_dense::detail::is_detected_v); + +using set1_t = ankerl::unordered_dense::set; +static_assert(!ankerl::unordered_dense::detail::is_detected_v); + +using set2_t = ankerl::unordered_dense::segmented_set; +static_assert(!ankerl::unordered_dense::detail::is_detected_v); diff --git a/test/unit/swap.cpp b/test/unit/swap.cpp index af69bac8..bf5dba62 100644 --- a/test/unit/swap.cpp +++ b/test/unit/swap.cpp @@ -1,17 +1,16 @@ #include #define ENABLE_LOG_LINE +#include #include -#include - #include -TEST_CASE("swap") { +TEST_CASE_MAP("swap", int, int) { { - auto b = ankerl::unordered_dense::map(); + auto b = map_t(); { - auto a = ankerl::unordered_dense::map(); + auto a = map_t(); b[1] = 2; a.swap(b); @@ -25,9 +24,9 @@ TEST_CASE("swap") { } { - auto a = ankerl::unordered_dense::map(); + auto a = map_t(); { - auto b = ankerl::unordered_dense::map(); + auto b = map_t(); b[1] = 2; a.swap(b); @@ -41,9 +40,9 @@ TEST_CASE("swap") { } { - auto a = ankerl::unordered_dense::map(); + auto a = map_t(); { - auto b = ankerl::unordered_dense::map(); + auto b = map_t(); a.swap(b); REQUIRE(a.end() == a.find(1)); REQUIRE(b.end() == b.find(1)); diff --git a/test/unit/transparent.cpp b/test/unit/transparent.cpp index c902f786..83d2f3e5 100644 --- a/test/unit/transparent.cpp +++ b/test/unit/transparent.cpp @@ -1,8 +1,8 @@ #include +#include #include -#include #include #include // for array @@ -24,9 +24,11 @@ class string_eq { using is_transparent = void; template + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay) auto operator()(T&& lhs, U&& rhs) const -> decltype(std::forward(lhs) == std::forward(rhs)) { auto names = fmt::format("{} -> {}", name_of_type(), name_of_type()); ++m_names_to_counts[names]; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay) return std::forward(lhs) == std::forward(rhs); } @@ -108,8 +110,10 @@ void check(int line, C const& container, size_t num_charstar, size_t num_stringv REQUIRE(counts[2] == num_string); } -TEST_CASE("transparent_find") { - auto map = ankerl::unordered_dense::map>(); +TYPE_TO_STRING_MAP(std::string, size_t, string_hash, std::equal_to<>); + +TEST_CASE_MAP("transparent_find", std::string, size_t, string_hash, std::equal_to<>) { + auto map = map_t(); map.try_emplace("hello", 1); check(__LINE__, map, 1, 0, 0); @@ -145,8 +149,8 @@ TEST_CASE("transparent_find") { check(__LINE__, map, 5, 2, 2); } -TEST_CASE("transparent_count") { - auto map = ankerl::unordered_dense::map>(); +TEST_CASE_MAP("transparent_count", std::string, size_t, string_hash, std::equal_to<>) { + auto map = map_t(); map.try_emplace("hello", 1); check(__LINE__, map, 1, 0, 0); @@ -166,8 +170,8 @@ TEST_CASE("transparent_count") { check(__LINE__, map, 3, 2, 2); } -TEST_CASE("transparent_contains") { - auto map = ankerl::unordered_dense::map>(); +TEST_CASE_MAP("transparent_contains", std::string, size_t, string_hash, std::equal_to<>) { + auto map = map_t(); map.try_emplace("hello", 1); check(__LINE__, map, 1, 0, 0); @@ -187,8 +191,8 @@ TEST_CASE("transparent_contains") { check(__LINE__, map, 3, 2, 2); } -TEST_CASE("transparent_erase") { - auto map = ankerl::unordered_dense::map>(); +TEST_CASE_MAP("transparent_erase", std::string, size_t, string_hash, std::equal_to<>) { + auto map = map_t(); map.try_emplace("hello", 1); check(__LINE__, map, 1, 0, 0); REQUIRE(0 == map.erase("huh")); @@ -211,8 +215,8 @@ TEST_CASE("transparent_erase") { check(__LINE__, map, 5, 2, 2); } -TEST_CASE("transparent_equal_range") { - auto map = ankerl::unordered_dense::map>(); +TEST_CASE_MAP("transparent_equal_range", std::string, size_t, string_hash, std::equal_to<>) { + auto map = map_t(); map.try_emplace("hello", 1); check(__LINE__, map, 1, 0, 0); @@ -229,8 +233,10 @@ TEST_CASE("transparent_equal_range") { REQUIRE(crange.second == map.end()); } -TEST_CASE("transparent_string_eq") { - auto map = ankerl::unordered_dense::map(); +TYPE_TO_STRING_MAP(std::string, size_t, string_hash, string_eq); + +TEST_CASE_MAP("transparent_string_eq", std::string, size_t, string_hash, string_eq) { + auto map = map_t(); map.try_emplace("hello", 1); REQUIRE(map.count("hello")); @@ -244,8 +250,8 @@ TEST_CASE("transparent_string_eq") { } } -TEST_CASE("transparent_at") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("transparent_at", std::string, size_t, string_hash, string_eq) { + auto map = map_t(); map.try_emplace("asdf", 123); check(__LINE__, map, 1, 0, 0); @@ -258,9 +264,11 @@ TEST_CASE("transparent_at") { check(__LINE__, map, 2, 1, 0); } -TEST_CASE("transparent_at_not") { +TYPE_TO_STRING_MAP(std::string, size_t, string_hash); + +TEST_CASE_MAP("transparent_at_not", std::string, size_t, string_hash) { // no string_eq, so not is_transparent - auto map = ankerl::unordered_dense::map(); + auto map = map_t(); map.try_emplace("asdf", 123); check(__LINE__, map, 0, 0, 1); @@ -273,8 +281,8 @@ TEST_CASE("transparent_at_not") { check(__LINE__, map, 0, 0, 3); } -TEST_CASE("transparent_insert_or_assign") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("transparent_insert_or_assign", std::string, size_t, string_hash, string_eq) { + auto map = map_t(); auto r = map.insert_or_assign("asdf", 123U); check(__LINE__, map, 1, 0, 0); REQUIRE(r.first->first == "asdf"); @@ -289,8 +297,8 @@ TEST_CASE("transparent_insert_or_assign") { REQUIRE(map.size() == 1U); } -TEST_CASE("transparent_insert_or_assign_not") { - auto map = ankerl::unordered_dense::map(); +TEST_CASE_MAP("transparent_insert_or_assign_not", std::string, size_t, string_hash) { + auto map = map_t(); auto r = map.insert_or_assign("asdf", 123U); check(__LINE__, map, 0, 0, 1); REQUIRE(r.first->first == "asdf"); @@ -305,48 +313,50 @@ TEST_CASE("transparent_insert_or_assign_not") { REQUIRE(map.size() == 1U); } -TEST_CASE("transparent_insert_or_assign_iterator") { - using map_t = ankerl::unordered_dense::map; +TEST_CASE_MAP("transparent_insert_or_assign_iterator", std::string, size_t, string_hash, string_eq) { auto map = map_t(); - auto r = map.insert_or_assign(map_t::const_iterator{}, "asdf", 123U); + auto r = map.insert_or_assign(typename map_t::const_iterator{}, "asdf", 123U); check(__LINE__, map, 1, 0, 0); REQUIRE(r->first == "asdf"); REQUIRE(r->second == 123U); - r = map.insert_or_assign(map_t::const_iterator{}, "asdf", 42U); + r = map.insert_or_assign(typename map_t::const_iterator{}, "asdf", 42U); check(__LINE__, map, 2, 0, 0); REQUIRE(r->first == "asdf"); REQUIRE(r->second == 42U); REQUIRE(map.size() == 1U); } -TEST_CASE("transparent_insert_or_assign_iterator_not") { - using map_t = ankerl::unordered_dense::map; +TEST_CASE_MAP("transparent_insert_or_assign_iterator_not", std::string, size_t, string_hash) { auto map = map_t(); - auto r = map.insert_or_assign(map_t::const_iterator{}, "asdf", 123U); + auto r = map.insert_or_assign(typename map_t::const_iterator{}, "asdf", 123U); check(__LINE__, map, 0, 0, 1); REQUIRE(r->first == "asdf"); REQUIRE(r->second == 123U); - r = map.insert_or_assign(map_t::const_iterator{}, "asdf", 42U); + r = map.insert_or_assign(typename map_t::const_iterator{}, "asdf", 42U); check(__LINE__, map, 0, 0, 2); REQUIRE(r->first == "asdf"); REQUIRE(r->second == 42U); REQUIRE(map.size() == 1U); } +TYPE_TO_STRING_SET(std::string, string_hash, string_eq); + // insert() transparent is only possible with unordered_set, not with unordered_map -TEST_CASE("transparent_set_insert") { - auto set = ankerl::unordered_dense::set(); +TEST_CASE_SET("transparent_set_insert", std::string, string_hash, string_eq) { + auto set = set_t(); set.insert("abcdefg"); check(__LINE__, set, 1, 0, 0); set.insert("abcdefg"); check(__LINE__, set, 2, 0, 0); } +TYPE_TO_STRING_SET(std::string, string_hash); + // insert() transparent is only possible with unordered_set, not with unordered_map -TEST_CASE("transparent_set_insert_not") { - auto set = ankerl::unordered_dense::set(); +TEST_CASE_SET("transparent_set_insert_not", std::string, string_hash) { + auto set = set_t(); set.insert("abcdefg"); check(__LINE__, set, 0, 0, 1); set.insert("abcdefg"); @@ -354,8 +364,8 @@ TEST_CASE("transparent_set_insert_not") { } // emplace() transparent is only possible with unordered_set, not with unordered_map -TEST_CASE("transparent_set_emplace") { - auto set = ankerl::unordered_dense::set(); +TEST_CASE_SET("transparent_set_emplace", std::string, string_hash, string_eq) { + auto set = set_t(); set.emplace("abcdefg"); check(__LINE__, set, 1, 0, 0); set.emplace("abcdefg"); @@ -363,8 +373,8 @@ TEST_CASE("transparent_set_emplace") { } // emplace() transparent is only possible with unordered_set, not with unordered_map -TEST_CASE("transparent_set_emplace_not") { - auto set = ankerl::unordered_dense::set(); +TEST_CASE_SET("transparent_set_emplace_not", std::string, string_hash) { + auto set = set_t(); set.emplace("abcdefg"); check(__LINE__, set, 0, 0, 1); set.emplace("abcdefg"); @@ -380,8 +390,10 @@ struct string_hash_simple { } }; -TEST_CASE("transparent_find_simple") { - auto map = ankerl::unordered_dense::map>(); +TYPE_TO_STRING_MAP(std::string, size_t, string_hash_simple, std::equal_to<>); + +TEST_CASE_MAP("transparent_find_simple", std::string, size_t, string_hash_simple, std::equal_to<>) { + auto map = map_t(); map.try_emplace("hello", 1); auto it = map.find("huh"); REQUIRE(it == map.end()); diff --git a/test/unit/try_emplace.cpp b/test/unit/try_emplace.cpp index b35cd8be..ce9f4545 100644 --- a/test/unit/try_emplace.cpp +++ b/test/unit/try_emplace.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include // for size_t #include // for allocator, string, operator== @@ -22,8 +22,9 @@ struct regular_type { std::size_t m_i; }; -TEST_CASE("try_emplace") { - using map_t = ankerl::unordered_dense::map; +TYPE_TO_STRING_MAP(std::string, regular_type); + +TEST_CASE_MAP("try_emplace", std::string, regular_type) { map_t map; auto ret = map.try_emplace("a", 1U, "b"); REQUIRE(ret.second); diff --git a/test/unit/unique_ptr.cpp b/test/unit/unique_ptr.cpp index e43e8f3d..c6dbef30 100644 --- a/test/unit/unique_ptr.cpp +++ b/test/unit/unique_ptr.cpp @@ -1,14 +1,15 @@ #include -#include +#include #include // for size_t #include // for operator!=, unique_ptr, make_unique #include // for move, pair #include // for vector -TEST_CASE("unique_ptr") { - using map_t = ankerl::unordered_dense::map>; +TYPE_TO_STRING_MAP(size_t, std::unique_ptr); + +TEST_CASE_MAP("unique_ptr", size_t, std::unique_ptr) { map_t m; REQUIRE(m.end() == m.find(123)); REQUIRE(m.end() == m.begin()); @@ -46,9 +47,7 @@ TEST_CASE("unique_ptr") { REQUIRE(m4.end() == m4.find(32)); } -TEST_CASE("unique_ptr_fill") { - using map_t = ankerl::unordered_dense::map>; - +TEST_CASE_MAP("unique_ptr_fill", size_t, std::unique_ptr) { map_t m; for (int i = 0; i < 1000; ++i) { // m.emplace(i % 500, std::make_unique(i)); diff --git a/test/unit/unordered_set.cpp b/test/unit/unordered_set.cpp index 2dc2d002..8a9588fa 100644 --- a/test/unit/unordered_set.cpp +++ b/test/unit/unordered_set.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include // for size_t #include // for UINT64_C, uint64_t @@ -9,14 +9,16 @@ #include // for vector TEST_CASE("unordered_set_asserts") { - using set_t = ankerl::unordered_dense::set; - static_assert(std::is_same::value, "key_type same"); - static_assert(std::is_same::value, "value_type same"); -} + using set1_t = ankerl::unordered_dense::set; + static_assert(std::is_same::value, "key_type same"); + static_assert(std::is_same::value, "value_type same"); -TEST_CASE("unordered_set") { - using set_t = ankerl::unordered_dense::set; + using set2_t = ankerl::unordered_dense::segmented_set; + static_assert(std::is_same::value, "key_type same"); + static_assert(std::is_same::value, "value_type same"); +} +TEST_CASE_SET("unordered_set", uint64_t) { set_t set; set.emplace(UINT64_C(123)); REQUIRE(set.size() == 1U); @@ -31,9 +33,7 @@ TEST_CASE("unordered_set") { REQUIRE(set.size() == 1U); } -TEST_CASE("unordered_set_string") { - using set_t = ankerl::unordered_dense::set; - +TEST_CASE_SET("unordered_set_string", std::string) { set_t set; REQUIRE(set.begin() == set.end()); @@ -48,9 +48,7 @@ TEST_CASE("unordered_set_string") { REQUIRE(++it == set.end()); } -TEST_CASE("unordered_set_eq") { - using set_t = ankerl::unordered_dense::set; - +TEST_CASE_SET("unordered_set_eq", std::string) { set_t set1; set_t set2; REQUIRE(set1.size() == set2.size()); diff --git a/test/unit/vectorofmaps.cpp b/test/unit/vectorofmaps.cpp index bed39bde..1f98fd5c 100644 --- a/test/unit/vectorofmaps.cpp +++ b/test/unit/vectorofmaps.cpp @@ -1,9 +1,9 @@ #include #include -#include +#include -#include +#include #include // for max #include // for size_t @@ -20,11 +20,10 @@ void fill(counter& counts, Map& map, ankerl::nanobench::Rng& rng) { } } -TEST_CASE("vectormap") { +TEST_CASE_MAP("vectormap", counter::obj, counter::obj) { auto counts = counter(); INFO(counts); - using map_t = ankerl::unordered_dense::map; auto rng = ankerl::nanobench::Rng(32154); { counts("begin"); diff --git a/test/unit/windows_include.cpp b/test/unit/windows_include.cpp index 965978b6..d53037e0 100644 --- a/test/unit/windows_include.cpp +++ b/test/unit/windows_include.cpp @@ -18,29 +18,29 @@ TEST_CASE("unordered_dense_with_windows_h") { auto map = map_t(); { - auto key = size_t(1); + auto key = size_t{1}; auto it = map.try_emplace(counter::obj(key, counts), counter::obj(key, counts)).first; REQUIRE(it != map.end()); REQUIRE(it->first.get() == key); } { - auto key = size_t(2); + auto key = size_t{2}; map.emplace(std::piecewise_construct, std::forward_as_tuple(key, counts), std::forward_as_tuple(key + 77, counts)); } { - auto key = size_t(3); + auto key = size_t{3}; map[counter::obj(key, counts)] = counter::obj(key + 123, counts); } { - auto key = size_t(4); + auto key = size_t{4}; map.insert(std::pair(counter::obj(key, counts), counter::obj(key, counts))); } { - auto key = size_t(5); + auto key = size_t{5}; map.insert_or_assign(counter::obj(key, counts), counter::obj(key + 1, counts)); } { - auto key = size_t(6); + auto key = size_t{6}; map.erase(counter::obj(key, counts)); } { map = map_t{}; } @@ -50,15 +50,15 @@ TEST_CASE("unordered_dense_with_windows_h") { } { map.clear(); } { - auto s = size_t(7); + auto s = size_t{7}; map.rehash(s); } { - auto s = size_t(8); + auto s = size_t{8}; map.reserve(s); } { - auto key = size_t(9); + auto key = size_t{9}; auto it = map.find(counter::obj(key, counts)); auto d = std::distance(map.begin(), it); REQUIRE(0 <= d);