diff options
| author | Nikolas Klauser <nikolasklauser@berlin.de> | 2025-08-29 18:48:11 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-29 18:48:11 +0200 |
| commit | 4c5877dbc2127e4948b9ef9a8c0beb4dd4b1fcf0 (patch) | |
| tree | f1b2c0038b734a5eac0829ec1f1d3e29a2f8bfc8 /libcxx/include/map | |
| parent | c9d7d10084ac1010d5c25a1ad70da5195b248b6f (diff) | |
[libc++] Optimize map::insert_or_assign (#155816)
`__emplace_unique` uses `__find_equal`, which can be significantly
faster than `lower_bound`. As a nice side-effect, this also changes the
implementation to the "naive" implementation of trying `insert` first,
and if that fails assign instead. This also matches the
`insert_or_assign` overloads with a hint.
```
Zen 2:
--------------------------------------------------------------------------------------------------------
Benchmark old new
--------------------------------------------------------------------------------------------------------
std::map<int, int>::insert_or_assign(key, value) (already present)/0 1.62 ns 1.53 ns
std::map<int, int>::insert_or_assign(key, value) (already present)/32 5.78 ns 5.99 ns
std::map<int, int>::insert_or_assign(key, value) (already present)/1024 21.5 ns 15.4 ns
std::map<int, int>::insert_or_assign(key, value) (already present)/8192 26.2 ns 20.5 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/0 22.5 ns 21.1 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/32 42.9 ns 28.4 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/1024 118 ns 92.0 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/8192 227 ns 173 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/0 13.2 ns 18.9 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/32 65.6 ns 39.0 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/1024 127 ns 64.4 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/8192 134 ns 71.4 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/0 45.6 ns 37.3 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/32 142 ns 93.3 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/1024 288 ns 147 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/8192 368 ns 182 ns
Apple M4:
--------------------------------------------------------------------------------------------------------
Benchmark old new
--------------------------------------------------------------------------------------------------------
std::map<int, int>::insert_or_assign(key, value) (already present)/0 0.784 ns 0.740 ns
std::map<int, int>::insert_or_assign(key, value) (already present)/32 2.52 ns 1.77 ns
std::map<int, int>::insert_or_assign(key, value) (already present)/1024 8.72 ns 4.06 ns
std::map<int, int>::insert_or_assign(key, value) (already present)/8192 10.6 ns 3.98 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/0 17.3 ns 17.2 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/32 22.5 ns 19.3 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/1024 56.8 ns 33.5 ns
std::map<int, int>::insert_or_assign(key, value) (new value)/8192 88.2 ns 41.0 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/0 16.6 ns 11.8 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/32 13.7 ns 30.7 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/1024 46.7 ns 49.1 ns
std::map<std::string, int>::insert_or_assign(key, value) (already present)/8192 41.9 ns 76.9 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/0 40.0 ns 40.5 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/32 38.9 ns 40.0 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/1024 84.9 ns 96.9 ns
std::map<std::string, int>::insert_or_assign(key, value) (new value)/8192 166 ns 149 ns
```
Diffstat (limited to 'libcxx/include/map')
| -rw-r--r-- | libcxx/include/map | 22 |
1 files changed, 10 insertions, 12 deletions
diff --git a/libcxx/include/map b/libcxx/include/map index b53e1c421348..395b434fe4a5 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -1144,22 +1144,20 @@ public: template <class _Vp> _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert_or_assign(const key_type& __k, _Vp&& __v) { - iterator __p = lower_bound(__k); - if (__p != end() && !key_comp()(__k, __p->first)) { - __p->second = std::forward<_Vp>(__v); - return std::make_pair(__p, false); - } - return std::make_pair(emplace_hint(__p, __k, std::forward<_Vp>(__v)), true); + auto __result = __tree_.__emplace_unique(__k, std::forward<_Vp>(__v)); + auto& [__iter, __inserted] = __result; + if (!__inserted) + __iter->second = std::forward<_Vp>(__v); + return __result; } template <class _Vp> _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert_or_assign(key_type&& __k, _Vp&& __v) { - iterator __p = lower_bound(__k); - if (__p != end() && !key_comp()(__k, __p->first)) { - __p->second = std::forward<_Vp>(__v); - return std::make_pair(__p, false); - } - return std::make_pair(emplace_hint(__p, std::move(__k), std::forward<_Vp>(__v)), true); + auto __result = __tree_.__emplace_unique(std::move(__k), std::forward<_Vp>(__v)); + auto& [__iter, __inserted] = __result; + if (!__inserted) + __iter->second = std::forward<_Vp>(__v); + return __result; } template <class _Vp> |
