Skip to content

Commit c6aec70

Browse files
committed
[libc++] std::cmp_less and other integer comparison functions could be improved
1 parent 3502063 commit c6aec70

File tree

2 files changed

+151
-12
lines changed

2 files changed

+151
-12
lines changed

libcxx/include/__utility/cmp.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2626

2727
#if _LIBCPP_STD_VER >= 20
2828

29+
template <typename _Tp, typename _Ip>
30+
concept __comparison_can_promote_to =
31+
sizeof(_Tp) < sizeof(_Ip) || (sizeof(_Tp) == sizeof(_Ip) && __signed_integer<_Tp>);
32+
2933
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
3034
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept {
31-
if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
32-
__builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
35+
if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
36+
return __t == __u;
37+
else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>)
3338
return static_cast<int>(__t) == static_cast<int>(__u);
34-
} else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
35-
__builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
39+
else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>)
3640
return static_cast<long long>(__t) == static_cast<long long>(__u);
37-
} else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
38-
return __t == __u;
3941
else if constexpr (is_signed_v<_Tp>)
4042
return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u;
4143
else
@@ -49,14 +51,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_not_equal(_Tp __t, _Up __u) noexcept {
4951

5052
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
5153
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_less(_Tp __t, _Up __u) noexcept {
52-
if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
53-
__builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
54+
if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
55+
return __t < __u;
56+
else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>)
5457
return static_cast<int>(__t) < static_cast<int>(__u);
55-
} else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
56-
__builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
58+
else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>)
5759
return static_cast<long long>(__t) < static_cast<long long>(__u);
58-
} else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
59-
return __t < __u;
6060
else if constexpr (is_signed_v<_Tp>)
6161
return __t < 0 ? true : make_unsigned_t<_Tp>(__t) < __u;
6262
else
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
11+
#include <utility>
12+
#include "../CartesianBenchmarks.h"
13+
#include "benchmark/benchmark.h"
14+
15+
namespace {
16+
17+
enum ValueType : size_t {
18+
SChar,
19+
UChar,
20+
Short,
21+
UShort,
22+
Int,
23+
UInt,
24+
Long,
25+
ULong,
26+
LongLong,
27+
ULongLong,
28+
#ifndef TEST_HAS_NO_INT128
29+
Int128,
30+
UInt128,
31+
#endif
32+
};
33+
34+
struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 6> {
35+
static constexpr const char* Names[] = {
36+
"schar",
37+
"uchar",
38+
"short",
39+
"ushort",
40+
"int",
41+
"uint",
42+
"long",
43+
"ulong",
44+
"longlong",
45+
"ulonglong",
46+
#ifndef TEST_HAS_NO_INT128
47+
"int128",
48+
"uint128"
49+
#endif
50+
};
51+
};
52+
53+
using TestType =
54+
std::tuple< signed char,
55+
unsigned char,
56+
short,
57+
unsigned short,
58+
int,
59+
unsigned int,
60+
long,
61+
unsigned long,
62+
long long,
63+
unsigned long long
64+
#ifndef TEST_HAS_NO_INT128
65+
,
66+
__int128_t,
67+
__uint128_t
68+
#endif
69+
>;
70+
71+
template <typename TType, typename UType>
72+
struct CmpEqual {
73+
static void run(benchmark::State& state) {
74+
using T = std::tuple_element_t<TType::value, TestType>;
75+
using U = std::tuple_element_t<UType::value, TestType>;
76+
77+
T x1 = T{127}, x2 = T{111};
78+
U y1 = U{123}, y2 = U{1};
79+
for (auto _ : state) {
80+
benchmark::DoNotOptimize(x1);
81+
benchmark::DoNotOptimize(x2);
82+
benchmark::DoNotOptimize(y1);
83+
benchmark::DoNotOptimize(y2);
84+
benchmark::DoNotOptimize(std::cmp_equal(x1, y1));
85+
benchmark::DoNotOptimize(std::cmp_equal(y1, x1));
86+
benchmark::DoNotOptimize(std::cmp_equal(x1, x1));
87+
benchmark::DoNotOptimize(std::cmp_equal(y1, y1));
88+
89+
benchmark::DoNotOptimize(std::cmp_equal(x2, y2));
90+
benchmark::DoNotOptimize(std::cmp_equal(y2, x2));
91+
benchmark::DoNotOptimize(std::cmp_equal(x2, x2));
92+
benchmark::DoNotOptimize(std::cmp_equal(y2, y2));
93+
}
94+
}
95+
96+
static std::string name() { return "BM_CmpEqual" + TType::name() + UType::name(); }
97+
};
98+
99+
template <typename TType, typename UType>
100+
struct CmpLess {
101+
static void run(benchmark::State& state) {
102+
using T = std::tuple_element_t<TType::value, TestType>;
103+
using U = std::tuple_element_t<UType::value, TestType>;
104+
105+
T x1 = T{127}, x2 = T{111};
106+
U y1 = U{123}, y2 = U{1};
107+
for (auto _ : state) {
108+
benchmark::DoNotOptimize(x1);
109+
benchmark::DoNotOptimize(x2);
110+
benchmark::DoNotOptimize(y1);
111+
benchmark::DoNotOptimize(y2);
112+
benchmark::DoNotOptimize(std::cmp_less(x1, y1));
113+
benchmark::DoNotOptimize(std::cmp_less(y1, x1));
114+
benchmark::DoNotOptimize(std::cmp_less(x1, x1));
115+
benchmark::DoNotOptimize(std::cmp_less(y1, y1));
116+
117+
benchmark::DoNotOptimize(std::cmp_less(x2, y2));
118+
benchmark::DoNotOptimize(std::cmp_less(y2, x2));
119+
benchmark::DoNotOptimize(std::cmp_less(x2, x2));
120+
benchmark::DoNotOptimize(std::cmp_less(y2, y2));
121+
}
122+
}
123+
124+
static std::string name() { return "BM_CmpLess" + TType::name() + UType::name(); }
125+
};
126+
127+
} // namespace
128+
129+
int main(int argc, char** argv) {
130+
benchmark::Initialize(&argc, argv);
131+
if (benchmark::ReportUnrecognizedArguments(argc, argv))
132+
return 1;
133+
134+
makeCartesianProductBenchmark<CmpEqual, AllValueTypes, AllValueTypes>();
135+
makeCartesianProductBenchmark<CmpLess, AllValueTypes, AllValueTypes>();
136+
benchmark::RunSpecifiedBenchmarks();
137+
138+
return 0;
139+
}

0 commit comments

Comments
 (0)