Skip to content

Commit 833d5f0

Browse files
authored
[libc++] Avoid constructing additional objects when using map::at (#157866)
This patch adds additional overloads to `map::at` in case its known that the argument is transparently comparable to the key type. This avoids actually constructing the key type in some cases, potentially removing allocations. ``` -------------------------------------------------------- Benchmark old new -------------------------------------------------------- BM_map_find_string_literal 12.8 ns 2.68 ns ```
1 parent ed07b30 commit 833d5f0

File tree

13 files changed

+211
-8
lines changed

13 files changed

+211
-8
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ set(files
839839
__type_traits/is_floating_point.h
840840
__type_traits/is_function.h
841841
__type_traits/is_fundamental.h
842+
__type_traits/is_generic_transparent_comparator.h
842843
__type_traits/is_implicit_lifetime.h
843844
__type_traits/is_implicitly_default_constructible.h
844845
__type_traits/is_integral.h
@@ -881,6 +882,7 @@ set(files
881882
__type_traits/make_32_64_or_128_bit.h
882883
__type_traits/make_const_lvalue_ref.h
883884
__type_traits/make_signed.h
885+
__type_traits/make_transparent.h
884886
__type_traits/make_unsigned.h
885887
__type_traits/maybe_const.h
886888
__type_traits/nat.h

libcxx/include/__algorithm/comp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__config>
1313
#include <__type_traits/desugars_to.h>
14+
#include <__type_traits/is_generic_transparent_comparator.h>
1415
#include <__type_traits/is_integral.h>
1516

1617
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -48,6 +49,9 @@ inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
4849
template <class _Tp>
4950
inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> = is_integral<_Tp>::value;
5051

52+
template <>
53+
inline const bool __is_generic_transparent_comparator_v<__less<> > = true;
54+
5155
_LIBCPP_END_NAMESPACE_STD
5256

5357
#endif // _LIBCPP___ALGORITHM_COMP_H

libcxx/include/__functional/is_transparent.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ inline const bool __is_transparent_v<_Tp, _Key, __void_t<typename _Tp::is_transp
2929

3030
#endif
3131

32+
// Two types are considered transparently comparable if `comparator(key, arg)` is equivalent to `comparator(key,
33+
// <implicit cast to KeyT>(arg))`.
34+
//
35+
// This is different from `__is_transparent_v`, which is only a property of the comparator and doesn't provide
36+
// additional semantic guarantees.
37+
template <class _Comparator, class _KeyT, class _Arg, class = void>
38+
inline const bool __is_transparently_comparable_v = false;
39+
3240
_LIBCPP_END_NAMESPACE_STD
3341

3442
#endif // _LIBCPP___FUNCTIONAL_IS_TRANSPARENT

libcxx/include/__functional/operations.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#include <__functional/unary_function.h>
1616
#include <__fwd/functional.h>
1717
#include <__type_traits/desugars_to.h>
18+
#include <__type_traits/is_generic_transparent_comparator.h>
1819
#include <__type_traits/is_integral.h>
20+
#include <__type_traits/make_transparent.h>
1921
#include <__utility/forward.h>
2022

2123
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -377,6 +379,14 @@ struct less<void> {
377379
typedef void is_transparent;
378380
};
379381

382+
template <class _Tp>
383+
struct __make_transparent<less<_Tp> > {
384+
using type _LIBCPP_NODEBUG = less<>;
385+
};
386+
387+
template <>
388+
inline const bool __is_generic_transparent_comparator_v<less<>> = true;
389+
380390
template <class _Tp, class _Up>
381391
inline const bool __desugars_to_v<__less_tag, less<>, _Tp, _Up> = true;
382392

@@ -466,6 +476,14 @@ struct greater<void> {
466476

467477
template <class _Tp, class _Up>
468478
inline const bool __desugars_to_v<__greater_tag, greater<>, _Tp, _Up> = true;
479+
480+
template <class _Tp>
481+
struct __make_transparent<greater<_Tp>> {
482+
using type _LIBCPP_NODEBUG = greater<>;
483+
};
484+
485+
template <>
486+
inline const bool __is_generic_transparent_comparator_v<greater<>> = true;
469487
#endif
470488

471489
// Logical operations

libcxx/include/__functional/ranges_operations.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <__concepts/totally_ordered.h>
1515
#include <__config>
1616
#include <__type_traits/desugars_to.h>
17+
#include <__type_traits/is_generic_transparent_comparator.h>
1718
#include <__utility/forward.h>
1819

1920
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -108,6 +109,12 @@ inline const bool __desugars_to_v<__less_tag, ranges::less, _Tp, _Up> = true;
108109
template <class _Tp, class _Up>
109110
inline const bool __desugars_to_v<__greater_tag, ranges::greater, _Tp, _Up> = true;
110111

112+
template <>
113+
inline const bool __is_generic_transparent_comparator_v<ranges::less> = true;
114+
115+
template <>
116+
inline const bool __is_generic_transparent_comparator_v<ranges::greater> = true;
117+
111118
#endif // _LIBCPP_STD_VER >= 20
112119

113120
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__tree

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <__type_traits/is_same.h>
3535
#include <__type_traits/is_specialization.h>
3636
#include <__type_traits/is_swappable.h>
37+
#include <__type_traits/make_transparent.h>
3738
#include <__type_traits/remove_const.h>
3839
#include <__utility/forward.h>
3940
#include <__utility/lazy_synth_three_way_comparator.h>
@@ -1749,7 +1750,8 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
17491750
}
17501751

17511752
__node_base_pointer* __node_ptr = __root_ptr();
1752-
auto __comp = __lazy_synth_three_way_comparator<_Compare, _Key, value_type>(value_comp());
1753+
auto&& __transparent = std::__as_transparent(value_comp());
1754+
auto __comp = __lazy_synth_three_way_comparator<__make_transparent_t<_Compare>, _Key, value_type>(__transparent);
17531755

17541756
while (true) {
17551757
auto __comp_res = __comp(__v, __nd->__get_value());
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___TYPE_TRAITS_IS_GENERIC_TRANSPARENT_COMPARATOR_H
10+
#define _LIBCPP___TYPE_TRAITS_IS_GENERIC_TRANSPARENT_COMPARATOR_H
11+
12+
#include <__config>
13+
14+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
15+
# pragma GCC system_header
16+
#endif
17+
18+
_LIBCPP_BEGIN_NAMESPACE_STD
19+
20+
// This traits returns true if the given _Comparator is known to accept any two types for compaison. This is separate
21+
// from `__is_transparent_v`, since that only enables overloads of specific functions, but doesn't give any semantic
22+
// guarantees. This trait guarantess that the comparator simply calls the appropriate comparison functions for any two
23+
// types.
24+
25+
template <class _Comparator>
26+
inline const bool __is_generic_transparent_comparator_v = false;
27+
28+
_LIBCPP_END_NAMESPACE_STD
29+
30+
#endif // _LIBCPP___TYPE_TRAITS_IS_GENERIC_TRANSPARENT_COMPARATOR_H
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___TYPE_TRAITS_MAKE_TRANSPARENT_H
10+
#define _LIBCPP___TYPE_TRAITS_MAKE_TRANSPARENT_H
11+
12+
#include <__config>
13+
#include <__type_traits/enable_if.h>
14+
#include <__type_traits/is_empty.h>
15+
#include <__type_traits/is_same.h>
16+
17+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
18+
# pragma GCC system_header
19+
#endif
20+
21+
_LIBCPP_BEGIN_NAMESPACE_STD
22+
23+
// __make_transparent tries to create a transparent comparator from its non-transparent counterpart, e.g. obtain
24+
// `less<>` from `less<T>`. This is useful in cases where conversions can be avoided (e.g. a string literal to a
25+
// std::string).
26+
27+
template <class _Comparator>
28+
struct __make_transparent {
29+
using type _LIBCPP_NODEBUG = _Comparator;
30+
};
31+
32+
template <class _Comparator>
33+
using __make_transparent_t _LIBCPP_NODEBUG = typename __make_transparent<_Comparator>::type;
34+
35+
template <class _Comparator, __enable_if_t<is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
36+
_LIBCPP_HIDE_FROM_ABI _Comparator& __as_transparent(_Comparator& __comp) {
37+
return __comp;
38+
}
39+
40+
template <class _Comparator, __enable_if_t<!is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
41+
_LIBCPP_HIDE_FROM_ABI __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
42+
static_assert(is_empty<_Comparator>::value);
43+
return __make_transparent_t<_Comparator>();
44+
}
45+
46+
_LIBCPP_END_NAMESPACE_STD
47+
48+
#endif // _LIBCPP___TYPE_TRAITS_MAKE_TRANSPARENT_H

libcxx/include/map

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,10 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred); // C++20
600600
# include <__ranges/from_range.h>
601601
# include <__tree>
602602
# include <__type_traits/container_traits.h>
603+
# include <__type_traits/desugars_to.h>
603604
# include <__type_traits/is_allocator.h>
605+
# include <__type_traits/is_convertible.h>
606+
# include <__type_traits/make_transparent.h>
604607
# include <__type_traits/remove_const.h>
605608
# include <__type_traits/type_identity.h>
606609
# include <__utility/forward.h>
@@ -666,6 +669,11 @@ public:
666669
# endif
667670
};
668671

672+
template <class _Key, class _MapValueT, class _Compare>
673+
struct __make_transparent<__map_value_compare<_Key, _MapValueT, _Compare> > {
674+
using type _LIBCPP_NODEBUG = __map_value_compare<_Key, _MapValueT, __make_transparent_t<_Compare> >;
675+
};
676+
669677
# if _LIBCPP_STD_VER >= 14
670678
template <class _MapValueT, class _Key, class _Compare>
671679
struct __lazy_synth_three_way_comparator<__map_value_compare<_Key, _MapValueT, _Compare>, _MapValueT, _MapValueT> {
@@ -1048,6 +1056,24 @@ public:
10481056
_LIBCPP_HIDE_FROM_ABI mapped_type& operator[](key_type&& __k);
10491057
# endif
10501058

1059+
template <class _Arg,
1060+
__enable_if_t<__is_transparently_comparable_v<_Compare, key_type, __remove_cvref_t<_Arg> >, int> = 0>
1061+
_LIBCPP_HIDE_FROM_ABI mapped_type& at(_Arg&& __arg) {
1062+
auto [_, __child] = __tree_.__find_equal(__arg);
1063+
if (__child == nullptr)
1064+
std::__throw_out_of_range("map::at: key not found");
1065+
return static_cast<__node_pointer>(__child)->__get_value().second;
1066+
}
1067+
1068+
template <class _Arg,
1069+
__enable_if_t<__is_transparently_comparable_v<_Compare, key_type, __remove_cvref_t<_Arg> >, int> = 0>
1070+
_LIBCPP_HIDE_FROM_ABI const mapped_type& at(_Arg&& __arg) const {
1071+
auto [_, __child] = __tree_.__find_equal(__arg);
1072+
if (__child == nullptr)
1073+
std::__throw_out_of_range("map::at: key not found");
1074+
return static_cast<__node_pointer>(__child)->__get_value().second;
1075+
}
1076+
10511077
_LIBCPP_HIDE_FROM_ABI mapped_type& at(const key_type& __k);
10521078
_LIBCPP_HIDE_FROM_ABI const mapped_type& at(const key_type& __k) const;
10531079

@@ -1242,11 +1268,15 @@ public:
12421268
_LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
12431269
_LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
12441270
# if _LIBCPP_STD_VER >= 14
1245-
template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
1271+
template <typename _K2,
1272+
enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
1273+
int> = 0>
12461274
_LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
12471275
return __tree_.find(__k);
12481276
}
1249-
template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
1277+
template <typename _K2,
1278+
enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
1279+
int> = 0>
12501280
_LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
12511281
return __tree_.find(__k);
12521282
}
@@ -1262,7 +1292,9 @@ public:
12621292

12631293
# if _LIBCPP_STD_VER >= 20
12641294
_LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
1265-
template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
1295+
template <typename _K2,
1296+
enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
1297+
int> = 0>
12661298
_LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
12671299
return find(__k) != end();
12681300
}
@@ -1271,12 +1303,16 @@ public:
12711303
_LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) { return __tree_.lower_bound(__k); }
12721304
_LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const { return __tree_.lower_bound(__k); }
12731305
# if _LIBCPP_STD_VER >= 14
1274-
template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
1306+
template <typename _K2,
1307+
enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
1308+
int> = 0>
12751309
_LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
12761310
return __tree_.lower_bound(__k);
12771311
}
12781312

1279-
template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
1313+
template <typename _K2,
1314+
enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
1315+
int> = 0>
12801316
_LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
12811317
return __tree_.lower_bound(__k);
12821318
}
@@ -1285,11 +1321,15 @@ public:
12851321
_LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.upper_bound(__k); }
12861322
_LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const { return __tree_.upper_bound(__k); }
12871323
# if _LIBCPP_STD_VER >= 14
1288-
template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
1324+
template <typename _K2,
1325+
enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
1326+
int> = 0>
12891327
_LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
12901328
return __tree_.upper_bound(__k);
12911329
}
1292-
template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
1330+
template <typename _K2,
1331+
enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
1332+
int> = 0>
12931333
_LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
12941334
return __tree_.upper_bound(__k);
12951335
}

libcxx/include/module.modulemap.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ module std_core [system] {
200200
header "__type_traits/is_fundamental.h"
201201
export std_core.type_traits.integral_constant
202202
}
203+
module is_generic_transparent_comparator { header "__type_traits/is_generic_transparent_comparator.h" }
203204
module is_implicit_lifetime {
204205
header "__type_traits/is_implicit_lifetime.h"
205206
export std_core.type_traits.integral_constant
@@ -353,6 +354,7 @@ module std_core [system] {
353354
module make_32_64_or_128_bit { header "__type_traits/make_32_64_or_128_bit.h" }
354355
module make_const_lvalue_ref { header "__type_traits/make_const_lvalue_ref.h" }
355356
module make_signed { header "__type_traits/make_signed.h" }
357+
module make_transparent { header "__type_traits/make_transparent.h" }
356358
module make_unsigned { header "__type_traits/make_unsigned.h" }
357359
module maybe_const { header "__type_traits/maybe_const.h" }
358360
module nat { header "__type_traits/nat.h" }

0 commit comments

Comments
 (0)