Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[libcxx] Avoid hash key in __hash_table::find() if it is empty. #126837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

xbcnn
Copy link

@xbcnn xbcnn commented Feb 12, 2025

If the hash table is empty, with or without buckets, the find() can do fast return. Then computing hash key is useless and avoidable, since it could be expensive for some key types, such as long strings.

This is a small optimization but useful in cases like a checklist (unordered_set/map) which is mostly empty.

1. With opt
----------------------------------------------------------------------------------------------------------------
Benchmark                                                                      Time             CPU   Iterations
----------------------------------------------------------------------------------------------------------------
std::unordered_set<int>::find(key) (expensive-empty)/32                     14.7 ns         14.7 ns     47768512
std::unordered_set<int>::find(key) (expensive-empty)/1024                   14.7 ns         14.7 ns     47753728
std::unordered_set<int>::find(key) (expensive-empty)/8192                   14.6 ns         14.6 ns     47774048
std::unordered_set<int>::count(key) (expensive-empty)/32                    19.4 ns         19.4 ns     36088896
std::unordered_set<int>::count(key) (expensive-empty)/1024                  19.4 ns         19.4 ns     36189728
std::unordered_set<int>::count(key) (expensive-empty)/8192                  19.4 ns         19.4 ns     35987328
std::unordered_set<int>::contains(key) (expensive-empty)/32                 21.4 ns         21.4 ns     32697888
std::unordered_set<int>::contains(key) (expensive-empty)/1024               21.4 ns         21.4 ns     32796896
std::unordered_set<int>::contains(key) (expensive-empty)/8192               21.4 ns         21.4 ns     32645952
std::unordered_set<std::string>::find(key) (expensive-empty)/32             14.4 ns         14.4 ns     48615200
std::unordered_set<std::string>::find(key) (expensive-empty)/1024           14.4 ns         14.4 ns     48589792
std::unordered_set<std::string>::find(key) (expensive-empty)/8192           14.4 ns         14.4 ns     48580512
std::unordered_set<std::string>::count(key) (expensive-empty)/32            19.3 ns         19.3 ns     36268384
std::unordered_set<std::string>::count(key) (expensive-empty)/1024          19.3 ns         19.3 ns     36256800
std::unordered_set<std::string>::count(key) (expensive-empty)/8192          19.3 ns         19.3 ns     36196064
std::unordered_set<std::string>::contains(key) (expensive-empty)/32         22.0 ns         22.0 ns     31811936
std::unordered_set<std::string>::contains(key) (expensive-empty)/1024       22.0 ns         22.0 ns     31827040
std::unordered_set<std::string>::contains(key) (expensive-empty)/8192       22.0 ns         22.0 ns     31805440

2. Without opt
----------------------------------------------------------------------------------------------------------------
Benchmark                                                                      Time             CPU   Iterations
----------------------------------------------------------------------------------------------------------------
std::unordered_set<int>::find(key) (expensive-empty)/32                     16.4 ns         16.4 ns     42864832
std::unordered_set<int>::find(key) (expensive-empty)/1024                   16.4 ns         16.4 ns     42698976
std::unordered_set<int>::find(key) (expensive-empty)/8192                   16.3 ns         16.3 ns     42920576
std::unordered_set<int>::count(key) (expensive-empty)/32                    21.5 ns         21.5 ns     32483104
std::unordered_set<int>::count(key) (expensive-empty)/1024                  21.5 ns         21.5 ns     32495072
std::unordered_set<int>::count(key) (expensive-empty)/8192                  21.6 ns         21.6 ns     32553600
std::unordered_set<int>::contains(key) (expensive-empty)/32                 23.1 ns         23.1 ns     30272224
std::unordered_set<int>::contains(key) (expensive-empty)/1024               23.1 ns         23.1 ns     30473984
std::unordered_set<int>::contains(key) (expensive-empty)/8192               23.2 ns         23.2 ns     30361024
std::unordered_set<std::string>::find(key) (expensive-empty)/32              314 ns          314 ns      2230656
std::unordered_set<std::string>::find(key) (expensive-empty)/1024            314 ns          314 ns      2229600
std::unordered_set<std::string>::find(key) (expensive-empty)/8192            314 ns          314 ns      2231328
std::unordered_set<std::string>::count(key) (expensive-empty)/32             318 ns          318 ns      2201024
std::unordered_set<std::string>::count(key) (expensive-empty)/1024           318 ns          318 ns      2198848
std::unordered_set<std::string>::count(key) (expensive-empty)/8192           318 ns          318 ns      2197472
std::unordered_set<std::string>::contains(key) (expensive-empty)/32          320 ns          320 ns      2188192
std::unordered_set<std::string>::contains(key) (expensive-empty)/1024        320 ns          320 ns      2187680
std::unordered_set<std::string>::contains(key) (expensive-empty)/8192        320 ns          320 ns      2182944

For expensive hash key types, i.e., long std::string, there is about 10x times faster.
For non-expensive int key, they are close, still a little faster.

If the hash table has no buckets yet, it's empty and the find will do fast
return end().  Then compute hash key is useless and can be avoided, since
it could be expensive for some key types, such as long string.

This is a small optimization but useful in cases like a checklist (
implemented as unordered_set) that is mostly empty.
@xbcnn xbcnn requested a review from a team as a code owner February 12, 2025 02:26
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 12, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 12, 2025

@llvm/pr-subscribers-libcxx

Author: None (xbcnn)

Changes

If the hash table has no buckets yet, it's empty and the find will do fast return end(). Then compute hash key is useless and can be avoided, since it could be expensive for some key types, such as long string.

This is a small optimization but useful in cases like a checklist ( implemented as unordered_set) that is mostly empty.


Full diff: https://github.com/llvm/llvm-project/pull/126837.diff

1 Files Affected:

  • (modified) libcxx/include/__hash_table (+2-2)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index d7b312f8774fc..a1d06d07f7c8d 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -1771,9 +1771,9 @@ template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _Key>
 typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::iterator
 __hash_table<_Tp, _Hash, _Equal, _Alloc>::find(const _Key& __k) {
-  size_t __hash  = hash_function()(__k);
   size_type __bc = bucket_count();
   if (__bc != 0) {
+    size_t __hash       = hash_function()(__k);
     size_t __chash      = std::__constrain_hash(__hash, __bc);
     __next_pointer __nd = __bucket_list_[__chash];
     if (__nd != nullptr) {
@@ -1792,9 +1792,9 @@ template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _Key>
 typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::const_iterator
 __hash_table<_Tp, _Hash, _Equal, _Alloc>::find(const _Key& __k) const {
-  size_t __hash  = hash_function()(__k);
   size_type __bc = bucket_count();
   if (__bc != 0) {
+    size_t __hash       = hash_function()(__k);
     size_t __chash      = std::__constrain_hash(__hash, __bc);
     __next_pointer __nd = __bucket_list_[__chash];
     if (__nd != nullptr) {

@xbcnn
Copy link
Author

xbcnn commented Feb 12, 2025

This is my first PR to the LLVM project. Please help to review and comments if any problem.
Thanks.

Copy link
Contributor

@frederick-vs-ja frederick-vs-ja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me!

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you provide some benchmarks for this change?

@xbcnn
Copy link
Author

xbcnn commented Feb 12, 2025

First thanks for reviews.

Could you provide some benchmarks for this change?

Sure, but I probably need some guides such as where to put the benchmark src and how to build/run it.

@philnik777
Copy link
Contributor

First thanks for reviews.

Could you provide some benchmarks for this change?

Sure, but I probably need some guides such as where to put the benchmark src and how to build/run it.

Benchmarks are in libcxx/test/benchmarks. If there are none for this you'll have to write your own. You can run benchmarks just like you run tests with lit, except that you want to pass --param=optimize=speed --show-all to optimize the benchmark and show the output.

@xbcnn
Copy link
Author

xbcnn commented Feb 13, 2025

I add a separate benchmark: libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp, since I need find on empty unordered_set.

I pre-generate 32K random strings(each 32-128 characters long), and do find iteration ranging from 1024~32768.
lit command: build/bin/llvm-lit --param=optimize=speed --show-all -sv build/runtimes/runtimes-bins/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp

With the opt:

# | ------------------------------------------------------------------------------------------
# | Benchmark                                                Time             CPU   Iterations
# | ------------------------------------------------------------------------------------------
# | BM_UnorderedSet_Find_EmptySet/long_string/1024       12276 ns        12276 ns        56892
# | BM_UnorderedSet_Find_EmptySet/long_string/2048       24622 ns        24622 ns        28436
# | BM_UnorderedSet_Find_EmptySet/long_string/4096       48973 ns        48972 ns        14249
# | BM_UnorderedSet_Find_EmptySet/long_string/8192       98178 ns        98175 ns         7116
# | BM_UnorderedSet_Find_EmptySet/long_string/16384     195976 ns       195965 ns         3569
# | BM_UnorderedSet_Find_EmptySet/long_string/32768     391325 ns       391318 ns         1782
# | BM_UnorderedSet_Find/long_string/1024               190627 ns       190621 ns         3672
# | BM_UnorderedSet_Find/long_string/2048               379007 ns       379003 ns         1847
# | BM_UnorderedSet_Find/long_string/4096               755937 ns       755924 ns          926
# | BM_UnorderedSet_Find/long_string/8192              1507339 ns      1507314 ns          464
# | BM_UnorderedSet_Find/long_string/16384             3019197 ns      3019036 ns          231
# | BM_UnorderedSet_Find/long_string/32768             6041986 ns      6041574 ns          116

Without the opt:

# | ------------------------------------------------------------------------------------------
# | Benchmark                                                Time             CPU   Iterations
# | ------------------------------------------------------------------------------------------
# | BM_UnorderedSet_Find_EmptySet/long_string/1024      163052 ns       163048 ns         4292
# | BM_UnorderedSet_Find_EmptySet/long_string/2048      324969 ns       324960 ns         2151
# | BM_UnorderedSet_Find_EmptySet/long_string/4096      647079 ns       647064 ns         1081
# | BM_UnorderedSet_Find_EmptySet/long_string/8192     1291289 ns      1291263 ns          542
# | BM_UnorderedSet_Find_EmptySet/long_string/16384    2592642 ns      2592585 ns          270
# | BM_UnorderedSet_Find_EmptySet/long_string/32768    5181394 ns      5181302 ns          135
# | BM_UnorderedSet_Find/long_string/1024               187192 ns       187189 ns         3740
# | BM_UnorderedSet_Find/long_string/2048               371351 ns       371346 ns         1883
# | BM_UnorderedSet_Find/long_string/4096               740937 ns       740902 ns          945
# | BM_UnorderedSet_Find/long_string/8192              1479457 ns      1479432 ns          473
# | BM_UnorderedSet_Find/long_string/16384             2962292 ns      2962147 ns          236
# | BM_UnorderedSet_Find/long_string/32768             5923977 ns      5923911 ns          118

On empty set, it's about 10+ times gains since no hash key computed.
On non-empty set, they're very close.

@philnik777
Copy link
Contributor

I think we could update associative_container_benchmarks.h instead to also run with zero elements. It looks like that's a useful metric. @ldionne any thoughts?

Copy link

github-actions bot commented Feb 13, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@xbcnn
Copy link
Author

xbcnn commented Feb 14, 2025

We could go ahead to double check size() != 0 before do actual lookup operations. Hash table could be already have buckets but still empty.

@xbcnn
Copy link
Author

xbcnn commented Feb 17, 2025

Updated benchmarks:

Opt with size() check

# | --------------------------------------------------------------------------------------------------
# | Benchmark                                                        Time             CPU   Iterations
# | --------------------------------------------------------------------------------------------------
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/1024         12121 ns        12120 ns        57902
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/2048         24171 ns        24170 ns        28959
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/4096         48279 ns        48277 ns        14452
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/8192         96207 ns        96204 ns         7221
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/16384       193415 ns       193404 ns         3611
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/32768       384792 ns       384784 ns         1820
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/1024       12823 ns        12822 ns        54793
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/2048       25686 ns        25683 ns        27212
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/4096       51277 ns        51275 ns        13573
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/8192      102201 ns       102200 ns         6820
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/16384     204915 ns       204908 ns         3405
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/32768     410902 ns       410897 ns         1707
# | BM_UnorderedSet_Find_NonEmpty/long_string/1024              191005 ns       190999 ns         3666
# | BM_UnorderedSet_Find_NonEmpty/long_string/2048              379653 ns       379640 ns         1844
# | BM_UnorderedSet_Find_NonEmpty/long_string/4096              756648 ns       756610 ns          925
# | BM_UnorderedSet_Find_NonEmpty/long_string/8192             1510045 ns      1509964 ns          464
# | BM_UnorderedSet_Find_NonEmpty/long_string/16384            3026213 ns      3026010 ns          231
# | BM_UnorderedSet_Find_NonEmpty/long_string/32768            6045257 ns      6044974 ns          116

No opt

# | --------------------------------------------------------------------------------------------------
# | Benchmark                                                        Time             CPU   Iterations
# | --------------------------------------------------------------------------------------------------
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/1024        162638 ns       162636 ns         4304
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/2048        323928 ns       323921 ns         2159
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/4096        645449 ns       645442 ns         1083
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/8192       1288309 ns      1288282 ns          544
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/16384      2585671 ns      2585642 ns          271
# | BM_UnorderedSet_Find_EmptyNoBuckets/long_string/32768      5299328 ns      5299122 ns          135
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/1024      169348 ns       169346 ns         4134
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/2048      337295 ns       337280 ns         2075
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/4096      671712 ns       671700 ns         1043
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/8192     1340185 ns      1340169 ns          523
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/16384    2689863 ns      2689757 ns          260
# | BM_UnorderedSet_Find_EmptyWithBuckets/long_string/32768    5378439 ns      5378379 ns          130
# | BM_UnorderedSet_Find_NonEmpty/long_string/1024              186529 ns       186524 ns         3751
# | BM_UnorderedSet_Find_NonEmpty/long_string/2048              370669 ns       370665 ns         1888
# | BM_UnorderedSet_Find_NonEmpty/long_string/4096              739087 ns       739044 ns          947
# | BM_UnorderedSet_Find_NonEmpty/long_string/8192             1476207 ns      1476178 ns          474
# | BM_UnorderedSet_Find_NonEmpty/long_string/16384            2953631 ns      2953560 ns          237
# | BM_UnorderedSet_Find_NonEmpty/long_string/32768            5902677 ns      5902474 ns          118

@xbcnn xbcnn requested review from philnik777 February 17, 2025 09:58
@xbcnn
Copy link
Author

xbcnn commented Feb 20, 2025

Hi @philnik777 @ldionne @frederick-vs-ja

I've added size() check along with buckets check and also updated the benchmarks.
Please help review. Thanks!

@xbcnn xbcnn changed the title [libcxx] Avoid hash key in __hash_table::find() if no buckets yet. [libcxx] Avoid hash key in __hash_table::find() if it is empty. Mar 6, 2025
@xbcnn xbcnn requested a review from frederick-vs-ja March 6, 2025 09:10
@xbcnn
Copy link
Author

xbcnn commented Apr 30, 2025

Hi @philnik777 @frederick-vs-ja
Just checking in to see if there’s anything I can do to help move this PR forward. It’s been open for some time, and I’d love to get your feedback. Let me know if changes are needed! Thanks for your time! 🙏

Copy link
Contributor

@frederick-vs-ja frederick-vs-ja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine. @philnik777

@@ -0,0 +1,97 @@
//===----------------------------------------------------------------------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update associative_container_benchmarks.h to include zero-sized benchmarks (where appropriate) instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I added with_expensive_key_empty to cover the tests and removed hash_table_find.bench.cpp

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to add ->Arg(0) to bench in associative_container_benchmarks.h?

  auto bench = [&](std::string operation, auto f) {
    benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(0)->Arg(32)->Arg(1024)->Arg(8192);
  };

For expensive key, such as std::unordered_set<std::string>, the benchmark reulst:

1. With the opt:

-------------------------------------------------------------------------------------------------------------------------------------
Benchmark                                                                                           Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------------------------------------
std::unordered_set<int>::ctor(const&)/32                                                         4717 ns         4718 ns       143456
std::unordered_set<int>::ctor(const&)/1024                                                     160766 ns       160764 ns         4384
std::unordered_set<int>::ctor(const&)/8192                                                    1362608 ns      1362523 ns          544
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/32                         6493 ns         6493 ns       104416
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/1024                     204541 ns       204539 ns         3456
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/8192                    1712671 ns      1712620 ns          416
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/32                           6533 ns         6534 ns       107136
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/1024                       202608 ns       202603 ns         3456
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/8192                      1707584 ns      1707476 ns          416
std::unordered_set<int>::operator=(const&)/32                                                    4814 ns         4814 ns       147424
std::unordered_set<int>::operator=(const&)/1024                                                154541 ns       154535 ns         4416
std::unordered_set<int>::operator=(const&)/8192                                               1310847 ns      1310769 ns          416
std::unordered_set<int>::insert(value) (already present)/32                                      57.5 ns         57.5 ns     12193408
std::unordered_set<int>::insert(value) (already present)/1024                                    60.4 ns         60.4 ns     11587808
std::unordered_set<int>::insert(value) (already present)/8192                                    60.7 ns         60.7 ns      9435360
std::unordered_set<int>::insert(value) (new value)/32                                             153 ns          154 ns      4433696
std::unordered_set<int>::insert(value) (new value)/1024                                           164 ns          164 ns      3980992
std::unordered_set<int>::insert(value) (new value)/8192                                           165 ns          165 ns      4045664
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/32                            6416 ns         6419 ns       102879
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/1024                        205733 ns       205748 ns         3425
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/8192                       1583461 ns      1583454 ns          443
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/32                           4363 ns         4370 ns       154592
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/1024                       141932 ns       141921 ns         5014
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/8192                      1096349 ns      1096330 ns          641
std::unordered_set<int>::erase(key) (existent)/32                                                 149 ns          149 ns      4715776
std::unordered_set<int>::erase(key) (existent)/1024                                               153 ns          153 ns      4587936
std::unordered_set<int>::erase(key) (existent)/8192                                               157 ns          157 ns      3200000
std::unordered_set<int>::erase(key) (non-existent)/32                                            48.6 ns         48.6 ns     14433088
std::unordered_set<int>::erase(key) (non-existent)/1024                                          55.4 ns         55.4 ns     14368768
std::unordered_set<int>::erase(key) (non-existent)/8192                                          50.1 ns         50.0 ns     12491488
std::unordered_set<int>::erase(iterator)/32                                                       106 ns          106 ns      6649824
std::unordered_set<int>::erase(iterator)/1024                                                     108 ns          107 ns      6551744
std::unordered_set<int>::erase(iterator)/8192                                                     108 ns          108 ns      6368928
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/32                 1869 ns         1861 ns       373187
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/1024              50300 ns        50256 ns        13894
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/8192             410477 ns       410379 ns         1707
std::unordered_set<int>::clear()/32                                                              1047 ns         1038 ns       672424
std::unordered_set<int>::clear()/1024                                                           24126 ns        24090 ns        29056
std::unordered_set<int>::clear()/8192                                                          188620 ns       188534 ns         3693
std::unordered_set<int>::find(key) (existent)/32                                                 52.2 ns         52.2 ns     13406688
std::unordered_set<int>::find(key) (existent)/1024                                               48.4 ns         48.4 ns     13773728
std::unordered_set<int>::find(key) (existent)/8192                                               55.8 ns         55.8 ns     12571776
std::unordered_set<int>::find(key) (non-existent)/32                                             41.0 ns         41.0 ns     14667808
std::unordered_set<int>::find(key) (non-existent)/1024                                           43.5 ns         43.5 ns     16278688
std::unordered_set<int>::find(key) (non-existent)/8192                                           41.2 ns         41.2 ns     15790752
std::unordered_set<int>::find(key) (expensive-empty)/32                                          14.6 ns         14.6 ns     47838272
std::unordered_set<int>::find(key) (expensive-empty)/1024                                        14.6 ns         14.6 ns     47741568
std::unordered_set<int>::find(key) (expensive-empty)/8192                                        14.7 ns         14.7 ns     47780224
std::unordered_set<int>::count(key) (existent)/32                                                58.4 ns         58.4 ns     12129728
std::unordered_set<int>::count(key) (existent)/1024                                              57.4 ns         57.4 ns     12091968
std::unordered_set<int>::count(key) (existent)/8192                                              61.1 ns         61.1 ns     12053312
std::unordered_set<int>::count(key) (non-existent)/32                                            60.7 ns         60.7 ns     13021152
std::unordered_set<int>::count(key) (non-existent)/1024                                          57.1 ns         57.1 ns     12352160
std::unordered_set<int>::count(key) (non-existent)/8192                                          61.1 ns         61.1 ns     14251424
std::unordered_set<int>::count(key) (expensive-empty)/32                                         19.4 ns         19.4 ns     36120640
std::unordered_set<int>::count(key) (expensive-empty)/1024                                       19.4 ns         19.4 ns     36122336
std::unordered_set<int>::count(key) (expensive-empty)/8192                                       19.4 ns         19.4 ns     35992544
std::unordered_set<int>::contains(key) (existent)/32                                             57.7 ns         57.7 ns     11540576
std::unordered_set<int>::contains(key) (existent)/1024                                           56.1 ns         56.1 ns     11500288
std::unordered_set<int>::contains(key) (existent)/8192                                           60.5 ns         60.5 ns     11040832
std::unordered_set<int>::contains(key) (non-existent)/32                                         58.9 ns         58.9 ns     13091488
std::unordered_set<int>::contains(key) (non-existent)/1024                                       53.0 ns         53.0 ns     12597184
std::unordered_set<int>::contains(key) (non-existent)/8192                                       56.1 ns         56.1 ns     12456960
std::unordered_set<int>::contains(key) (expensive-empty)/32                                      21.4 ns         21.4 ns     32681408
std::unordered_set<int>::contains(key) (expensive-empty)/1024                                    21.4 ns         21.4 ns     32516640
std::unordered_set<int>::contains(key) (expensive-empty)/8192                                    21.4 ns         21.4 ns     32573888
std::unordered_set<std::string>::ctor(const&)/32                                                24879 ns        24880 ns        25856
std::unordered_set<std::string>::ctor(const&)/1024                                             884089 ns       884042 ns          832
std::unordered_set<std::string>::ctor(const&)/8192                                            8585689 ns      8585383 ns           96
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32                29851 ns        29853 ns        22944
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024            1149105 ns      1149052 ns          768
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192            9769845 ns      9769243 ns           64
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/32                  33674 ns        33678 ns        26624
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024              1058258 ns      1058235 ns          544
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192              9624318 ns      9624047 ns           96
std::unordered_set<std::string>::operator=(const&)/32                                           48214 ns        48216 ns        15296
std::unordered_set<std::string>::operator=(const&)/1024                                       1523473 ns      1523422 ns          448
std::unordered_set<std::string>::operator=(const&)/8192                                      13500351 ns     13499850 ns           64
std::unordered_set<std::string>::insert(value) (already present)/32                               324 ns          324 ns      2154816
std::unordered_set<std::string>::insert(value) (already present)/1024                             731 ns          731 ns       964224
std::unordered_set<std::string>::insert(value) (already present)/8192                            1463 ns         1463 ns       460064
std::unordered_set<std::string>::insert(value) (new value)/32                                    1005 ns         1004 ns      1843488
std::unordered_set<std::string>::insert(value) (new value)/1024                                  1299 ns         1191 ns       636960
std::unordered_set<std::string>::insert(value) (new value)/8192                                  1726 ns         1702 ns       320000
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/32                   44153 ns        42927 ns        17413
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/1024               1476626 ns      1447248 ns          482
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/8192              13019197 ns     12896650 ns           55
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/32                  40150 ns        40125 ns        16551
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/1024              1407984 ns      1341443 ns          497
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/8192             12215376 ns     12203965 ns           57
std::unordered_set<std::string>::erase(key) (existent)/32                                        2569 ns         1997 ns       349312
std::unordered_set<std::string>::erase(key) (existent)/1024                                      1970 ns         1927 ns       930304
std::unordered_set<std::string>::erase(key) (existent)/8192                                      1621 ns         1618 ns       529216
std::unordered_set<std::string>::erase(key) (non-existent)/32                                    1174 ns          986 ns       623360
std::unordered_set<std::string>::erase(key) (non-existent)/1024                                  1194 ns         1048 ns       676672
std::unordered_set<std::string>::erase(key) (non-existent)/8192                                   983 ns          962 ns       688000
std::unordered_set<std::string>::erase(iterator)/32                                               213 ns          209 ns      3374560
std::unordered_set<std::string>::erase(iterator)/1024                                             212 ns          207 ns      3474080
std::unordered_set<std::string>::erase(iterator)/8192                                             218 ns          206 ns      3805824
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/32         2854 ns         2804 ns       208147
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/1024      64984 ns        64778 ns        10493
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/8192     695737 ns       695149 ns         1013
std::unordered_set<std::string>::clear()/32                                                      1388 ns         1370 ns       506834
std::unordered_set<std::string>::clear()/1024                                                   46217 ns        46073 ns        15204
std::unordered_set<std::string>::clear()/8192                                                  599831 ns       599498 ns         1180
std::unordered_set<std::string>::find(key) (existent)/32                                          753 ns          753 ns      1039520
std::unordered_set<std::string>::find(key) (existent)/1024                                        662 ns          662 ns       968320
std::unordered_set<std::string>::find(key) (existent)/8192                                        610 ns          610 ns      1020864
std::unordered_set<std::string>::find(key) (non-existent)/32                                      625 ns          625 ns      1117408
std::unordered_set<std::string>::find(key) (non-existent)/1024                                    655 ns          655 ns      1083008
std::unordered_set<std::string>::find(key) (non-existent)/8192                                    650 ns          650 ns      1042560
std::unordered_set<std::string>::find(key) (expensive-empty)/32                                  14.4 ns         14.4 ns     48641472
std::unordered_set<std::string>::find(key) (expensive-empty)/1024                                14.4 ns         14.4 ns     48623392
std::unordered_set<std::string>::find(key) (expensive-empty)/8192                                14.4 ns         14.4 ns     48591616
std::unordered_set<std::string>::count(key) (existent)/32                                         824 ns          824 ns       985760
std::unordered_set<std::string>::count(key) (existent)/1024                                       776 ns          776 ns       929376
std::unordered_set<std::string>::count(key) (existent)/8192                                       703 ns          702 ns       995936
std::unordered_set<std::string>::count(key) (non-existent)/32                                     725 ns          725 ns      1087584
std::unordered_set<std::string>::count(key) (non-existent)/1024                                   709 ns          709 ns       937664
std::unordered_set<std::string>::count(key) (non-existent)/8192                                   625 ns          625 ns      1144000
std::unordered_set<std::string>::count(key) (expensive-empty)/32                                 19.3 ns         19.3 ns     36164832
std::unordered_set<std::string>::count(key) (expensive-empty)/1024                               19.3 ns         19.3 ns     36393312
std::unordered_set<std::string>::count(key) (expensive-empty)/8192                               19.3 ns         19.3 ns     36143328
std::unordered_set<std::string>::contains(key) (existent)/32                                      709 ns          709 ns      1048064
std::unordered_set<std::string>::contains(key) (existent)/1024                                    748 ns          748 ns      1024768
std::unordered_set<std::string>::contains(key) (existent)/8192                                    831 ns          831 ns       895008
std::unordered_set<std::string>::contains(key) (non-existent)/32                                  714 ns          714 ns      1075264
std::unordered_set<std::string>::contains(key) (non-existent)/1024                                504 ns          504 ns      1222240
std::unordered_set<std::string>::contains(key) (non-existent)/8192                                717 ns          717 ns      1022432
std::unordered_set<std::string>::contains(key) (expensive-empty)/32                              22.0 ns         22.0 ns     31752096
std::unordered_set<std::string>::contains(key) (expensive-empty)/1024                            22.0 ns         22.0 ns     31857600
std::unordered_set<std::string>::contains(key) (expensive-empty)/8192                            22.0 ns         22.0 ns     31783040


2. Without the opt:

-------------------------------------------------------------------------------------------------------------------------------------
Benchmark                                                                                           Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------------------------------------
std::unordered_set<int>::ctor(const&)/32                                                         4920 ns         4921 ns       142304
std::unordered_set<int>::ctor(const&)/1024                                                     161004 ns       161004 ns         4384
std::unordered_set<int>::ctor(const&)/8192                                                    1369984 ns      1369913 ns          512
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/32                         6583 ns         6584 ns       105536
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/1024                     203335 ns       203328 ns         3488
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/8192                    1712738 ns      1712659 ns          416
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/32                           6417 ns         6418 ns       105920
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/1024                       204252 ns       204248 ns         3456
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/8192                      1718282 ns      1718226 ns          416
std::unordered_set<int>::operator=(const&)/32                                                    4777 ns         4778 ns       143776
std::unordered_set<int>::operator=(const&)/1024                                                155375 ns       155370 ns         4416
std::unordered_set<int>::operator=(const&)/8192                                               1318345 ns      1318318 ns          416
std::unordered_set<int>::insert(value) (already present)/32                                      57.1 ns         57.1 ns     12182624
std::unordered_set<int>::insert(value) (already present)/1024                                    60.2 ns         60.2 ns     11640704
std::unordered_set<int>::insert(value) (already present)/8192                                    59.3 ns         59.3 ns     11515456
std::unordered_set<int>::insert(value) (new value)/32                                             152 ns          152 ns      4575936
std::unordered_set<int>::insert(value) (new value)/1024                                           161 ns          161 ns      4283488
std::unordered_set<int>::insert(value) (new value)/8192                                           172 ns          172 ns      4288736
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/32                            6430 ns         6434 ns       107732
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/1024                        206167 ns       206184 ns         3429
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/8192                       1587621 ns      1587634 ns          417
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/32                           4588 ns         4587 ns       150781
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/1024                       132835 ns       132832 ns         4951
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/8192                      1095236 ns      1095210 ns          641
std::unordered_set<int>::erase(key) (existent)/32                                                 154 ns          154 ns      4492416
std::unordered_set<int>::erase(key) (existent)/1024                                               160 ns          160 ns      4383520
std::unordered_set<int>::erase(key) (existent)/8192                                               155 ns          155 ns      4363744
std::unordered_set<int>::erase(key) (non-existent)/32                                            57.8 ns         57.8 ns     14141664
std::unordered_set<int>::erase(key) (non-existent)/1024                                          57.4 ns         57.4 ns     14369216
std::unordered_set<int>::erase(key) (non-existent)/8192                                          56.4 ns         56.4 ns     13152576
std::unordered_set<int>::erase(iterator)/32                                                       108 ns          108 ns      6519872
std::unordered_set<int>::erase(iterator)/1024                                                     110 ns          110 ns      6271040
std::unordered_set<int>::erase(iterator)/8192                                                     112 ns          112 ns      6295872
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/32                 1815 ns         1808 ns       363644
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/1024              51034 ns        51007 ns        13752
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/8192             414144 ns       414067 ns         1692
std::unordered_set<int>::clear()/32                                                              1053 ns         1049 ns       660370
std::unordered_set<int>::clear()/1024                                                           24122 ns        24110 ns        29062
std::unordered_set<int>::clear()/8192                                                          189528 ns       189457 ns         3704
std::unordered_set<int>::find(key) (existent)/32                                                 52.0 ns         52.0 ns     12869664
std::unordered_set<int>::find(key) (existent)/1024                                               51.0 ns         51.0 ns     13863392
std::unordered_set<int>::find(key) (existent)/8192                                               52.8 ns         52.8 ns     13966656
std::unordered_set<int>::find(key) (non-existent)/32                                             43.0 ns         43.0 ns     13238752
std::unordered_set<int>::find(key) (non-existent)/1024                                           46.9 ns         46.9 ns     17560800
std::unordered_set<int>::find(key) (non-existent)/8192                                           40.5 ns         40.5 ns     17009920
std::unordered_set<int>::find(key) (expensive-empty)/32                                          16.4 ns         16.4 ns     42983616
std::unordered_set<int>::find(key) (expensive-empty)/1024                                        16.5 ns         16.5 ns     42652352
std::unordered_set<int>::find(key) (expensive-empty)/8192                                        16.3 ns         16.3 ns     42610592
std::unordered_set<int>::count(key) (existent)/32                                                58.6 ns         58.6 ns     11978688
std::unordered_set<int>::count(key) (existent)/1024                                              56.3 ns         56.3 ns     12275488
std::unordered_set<int>::count(key) (existent)/8192                                              61.3 ns         61.3 ns     11718880
std::unordered_set<int>::count(key) (non-existent)/32                                            51.6 ns         51.6 ns     13867456
std::unordered_set<int>::count(key) (non-existent)/1024                                          59.7 ns         59.7 ns     12946400
std::unordered_set<int>::count(key) (non-existent)/8192                                          57.9 ns         57.9 ns     12481344
std::unordered_set<int>::count(key) (expensive-empty)/32                                         21.5 ns         21.5 ns     32519840
std::unordered_set<int>::count(key) (expensive-empty)/1024                                       21.5 ns         21.5 ns     32430592
std::unordered_set<int>::count(key) (expensive-empty)/8192                                       21.5 ns         21.5 ns     32623872
std::unordered_set<int>::contains(key) (existent)/32                                             58.3 ns         58.3 ns     11878944
std::unordered_set<int>::contains(key) (existent)/1024                                           61.1 ns         61.1 ns     11275648
std::unordered_set<int>::contains(key) (existent)/8192                                           59.8 ns         59.8 ns     11298112
std::unordered_set<int>::contains(key) (non-existent)/32                                         52.4 ns         52.4 ns     11874720
std::unordered_set<int>::contains(key) (non-existent)/1024                                       56.1 ns         56.1 ns     11843904
std::unordered_set<int>::contains(key) (non-existent)/8192                                       57.3 ns         57.3 ns     10858048
std::unordered_set<int>::contains(key) (expensive-empty)/32                                      23.1 ns         23.1 ns     30402240
std::unordered_set<int>::contains(key) (expensive-empty)/1024                                    23.1 ns         23.1 ns     30244128
std::unordered_set<int>::contains(key) (expensive-empty)/8192                                    23.1 ns         23.1 ns     30230528
std::unordered_set<std::string>::ctor(const&)/32                                                26497 ns        26497 ns        25280
std::unordered_set<std::string>::ctor(const&)/1024                                             825878 ns       825839 ns          832
std::unordered_set<std::string>::ctor(const&)/8192                                            8670858 ns      8670549 ns           96
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32                27310 ns        27311 ns        26464
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024             946828 ns       946803 ns          768
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192            9053635 ns      9053223 ns           96
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/32                  27548 ns        27548 ns        23648
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024               898692 ns       898659 ns          800
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192              9010872 ns      9010567 ns           96
std::unordered_set<std::string>::operator=(const&)/32                                           47648 ns        47650 ns        14080
std::unordered_set<std::string>::operator=(const&)/1024                                       1475556 ns      1475497 ns          448
std::unordered_set<std::string>::operator=(const&)/8192                                      13089133 ns     13088427 ns           64
std::unordered_set<std::string>::insert(value) (already present)/32                               946 ns          946 ns       796928
std::unordered_set<std::string>::insert(value) (already present)/1024                             729 ns          729 ns      2565824
std::unordered_set<std::string>::insert(value) (already present)/8192                             805 ns          805 ns      2718624
std::unordered_set<std::string>::insert(value) (new value)/32                                    1161 ns         1161 ns      1403360
std::unordered_set<std::string>::insert(value) (new value)/1024                                  1246 ns         1246 ns      1978080
std::unordered_set<std::string>::insert(value) (new value)/8192                                  1110 ns         1110 ns       985376
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/32                   30256 ns        30276 ns        25741
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/1024                908942 ns       909062 ns          745
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/8192               7286785 ns      7286437 ns           96
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/32                  26705 ns        26699 ns        26325
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/1024               837638 ns       837685 ns          835
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/8192              6982924 ns      6982639 ns          100
std::unordered_set<std::string>::erase(key) (existent)/32                                        1082 ns         1082 ns      1315040
std::unordered_set<std::string>::erase(key) (existent)/1024                                       815 ns          815 ns      1036096
std::unordered_set<std::string>::erase(key) (existent)/8192                                      1004 ns         1004 ns       868960
std::unordered_set<std::string>::erase(key) (non-existent)/32                                     632 ns          632 ns      1007424
std::unordered_set<std::string>::erase(key) (non-existent)/1024                                   609 ns          609 ns      1200256
std::unordered_set<std::string>::erase(key) (non-existent)/8192                                   749 ns          749 ns      1040416
std::unordered_set<std::string>::erase(iterator)/32                                               132 ns          131 ns      5600064
std::unordered_set<std::string>::erase(iterator)/1024                                             125 ns          125 ns      5720800
std::unordered_set<std::string>::erase(iterator)/8192                                             130 ns          130 ns      5698336
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/32         2188 ns         2166 ns       329336
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/1024      66079 ns        65889 ns        10574
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/8192     685952 ns       685607 ns         1001
std::unordered_set<std::string>::clear()/32                                                      1388 ns         1372 ns       507157
std::unordered_set<std::string>::clear()/1024                                                   47859 ns        47698 ns        15356
std::unordered_set<std::string>::clear()/8192                                                  573679 ns       573370 ns         1137
std::unordered_set<std::string>::find(key) (existent)/32                                          728 ns          728 ns       955840
std::unordered_set<std::string>::find(key) (existent)/1024                                        682 ns          682 ns       926912
std::unordered_set<std::string>::find(key) (existent)/8192                                        648 ns          648 ns       923872
std::unordered_set<std::string>::find(key) (non-existent)/32                                      646 ns          646 ns       958112
std::unordered_set<std::string>::find(key) (non-existent)/1024                                    702 ns          702 ns      1090208
std::unordered_set<std::string>::find(key) (non-existent)/8192                                    594 ns          594 ns      1150432
std::unordered_set<std::string>::find(key) (expensive-empty)/32                                   313 ns          313 ns      2236000
std::unordered_set<std::string>::find(key) (expensive-empty)/1024                                 313 ns          313 ns      2237376
std::unordered_set<std::string>::find(key) (expensive-empty)/8192                                 313 ns          313 ns      2233696
std::unordered_set<std::string>::count(key) (existent)/32                                         803 ns          803 ns       987712
std::unordered_set<std::string>::count(key) (existent)/1024                                       670 ns          670 ns       919680
std::unordered_set<std::string>::count(key) (existent)/8192                                       668 ns          668 ns       878944
std::unordered_set<std::string>::count(key) (non-existent)/32                                     706 ns          706 ns       966848
std::unordered_set<std::string>::count(key) (non-existent)/1024                                   558 ns          558 ns       979904
std::unordered_set<std::string>::count(key) (non-existent)/8192                                   632 ns          632 ns      1027840
std::unordered_set<std::string>::count(key) (expensive-empty)/32                                  319 ns          319 ns      2196512
std::unordered_set<std::string>::count(key) (expensive-empty)/1024                                318 ns          318 ns      2199328
std::unordered_set<std::string>::count(key) (expensive-empty)/8192                                318 ns          318 ns      2197472
std::unordered_set<std::string>::contains(key) (existent)/32                                      708 ns          708 ns      1037120
std::unordered_set<std::string>::contains(key) (existent)/1024                                    765 ns          765 ns       889856
std::unordered_set<std::string>::contains(key) (existent)/8192                                    740 ns          740 ns       945600
std::unordered_set<std::string>::contains(key) (non-existent)/32                                  730 ns          730 ns      1077984
std::unordered_set<std::string>::contains(key) (non-existent)/1024                                662 ns          662 ns      1001536
std::unordered_set<std::string>::contains(key) (non-existent)/8192                                662 ns          662 ns      1024864
std::unordered_set<std::string>::contains(key) (expensive-empty)/32                               321 ns          321 ns      2182656
std::unordered_set<std::string>::contains(key) (expensive-empty)/1024                             320 ns          320 ns      2188096
std::unordered_set<std::string>::contains(key) (expensive-empty)/8192                             320 ns          320 ns      2188192
@xbcnn
Copy link
Author

xbcnn commented May 8, 2025

Sorry for delayed response as I was on vacation for the past days.
Please help review the changes again. @philnik777

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants