[libcxx-commits] [libcxx] [libc++] std::cmp_less and other integer comparison functions could be improved (PR #151332)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Sep 9 06:41:07 PDT 2025
https://github.com/kisuhorikka updated https://github.com/llvm/llvm-project/pull/151332
>From 711144331e712f31e09d71ecbfbca12b4d07e8e1 Mon Sep 17 00:00:00 2001
From: kisuhorikka <kisuhorikka at gmail.com>
Date: Wed, 30 Jul 2025 20:53:05 +0800
Subject: [PATCH 1/2] [libc++] std::cmp_less and other integer comparison
functions could be improved
---
libcxx/include/__utility/cmp.h | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 14dc0c154c040..4c29f09628095 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -28,7 +28,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept {
- if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
+ __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ return static_cast<int>(__t) == static_cast<int>(__u);
+ } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
+ __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ return static_cast<long long>(__t) == static_cast<long long>(__u);
+ } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
return __t == __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u;
@@ -43,7 +49,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_not_equal(_Tp __t, _Up __u) noexcept {
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_less(_Tp __t, _Up __u) noexcept {
- if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
+ __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ return static_cast<int>(__t) < static_cast<int>(__u);
+ } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
+ __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ return static_cast<long long>(__t) < static_cast<long long>(__u);
+ } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
return __t < __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? true : make_unsigned_t<_Tp>(__t) < __u;
>From e31deedf0c709cf9b6869b481e3bfc5a81735561 Mon Sep 17 00:00:00 2001
From: kisuhorikka <kisuhorikka at gmail.com>
Date: Thu, 31 Jul 2025 13:08:18 +0800
Subject: [PATCH 2/2] [libc++] std::cmp_less and other integer comparison
functions could be improved with _LIBCPP_ASSUME |
-------------------------------------------------------------------- |
Benchmark Time CPU Iterations |
-------------------------------------------------------------------- |
BM_CmpEqual_schar_schar 0.221 ns 0.221 ns 3170066917 |
BM_CmpEqual_schar_uchar 0.232 ns 0.232 ns 3025025997 |
BM_CmpEqual_schar_short 0.280 ns 0.280 ns 2507488723 |
BM_CmpEqual_schar_ushort 0.361 ns 0.361 ns 2086768552 |
BM_CmpEqual_schar_int 0.220 ns 0.220 ns 3123871432 |
BM_CmpEqual_schar_uint 0.233 ns 0.233 ns 3023305113 |
BM_CmpEqual_uchar_schar 0.221 ns 0.221 ns 3180711887 |
BM_CmpEqual_uchar_uchar 0.220 ns 0.220 ns 3177773411 |
BM_CmpEqual_uchar_short 0.221 ns 0.221 ns 3178208863 |
BM_CmpEqual_uchar_ushort 0.295 ns 0.295 ns 2331090257 |
BM_CmpEqual_uchar_int 0.221 ns 0.221 ns 3156844998 |
BM_CmpEqual_uchar_uint 0.220 ns 0.220 ns 3169867220 |
BM_CmpEqual_short_schar 0.662 ns 0.662 ns 1057277878 |
BM_CmpEqual_short_uchar 0.232 ns 0.232 ns 3019119818 |
BM_CmpEqual_short_short 0.222 ns 0.222 ns 3171932178 |
BM_CmpEqual_short_ushort 0.282 ns 0.282 ns 2495630490 |
BM_CmpEqual_short_int 0.221 ns 0.221 ns 3165298446 |
BM_CmpEqual_short_uint 0.368 ns 0.368 ns 2151247764 |
BM_CmpEqual_ushort_schar 0.280 ns 0.280 ns 2503964124 |
BM_CmpEqual_ushort_uchar 0.221 ns 0.221 ns 3164632430 |
BM_CmpEqual_ushort_short 0.281 ns 0.281 ns 2502613193 |
BM_CmpEqual_ushort_ushort 0.221 ns 0.221 ns 3164295092 |
BM_CmpEqual_ushort_int 0.221 ns 0.221 ns 3170047435 |
BM_CmpEqual_ushort_uint 0.222 ns 0.222 ns 3172795286 |
BM_CmpEqual_int_schar 0.221 ns 0.221 ns 3170067649 |
BM_CmpEqual_int_uchar 0.221 ns 0.221 ns 3162764124 |
BM_CmpEqual_int_short 0.222 ns 0.222 ns 3149384461 |
BM_CmpEqual_int_ushort 0.221 ns 0.221 ns 3170863296 |
BM_CmpEqual_int_int 0.221 ns 0.221 ns 3173623511 |
BM_CmpEqual_int_uint 0.349 ns 0.349 ns 2074548798 |
BM_CmpEqual_uint_schar 0.221 ns 0.221 ns 3172609898 |
BM_CmpEqual_uint_uchar 0.221 ns 0.221 ns 3167865061 |
BM_CmpEqual_uint_short 0.221 ns 0.221 ns 3166421901 |
BM_CmpEqual_uint_ushort 0.221 ns 0.221 ns 3154601627 |
BM_CmpEqual_uint_int 0.222 ns 0.222 ns 3170922460 |
BM_CmpEqual_uint_uint 0.221 ns 0.221 ns 3178919527 |
BM_CmpLess_schar_schar 0.220 ns 0.220 ns 3166946919 |
BM_CmpLess_schar_uchar 0.328 ns 0.328 ns 1989530669 |
BM_CmpLess_schar_short 0.280 ns 0.280 ns 2481635006 |
BM_CmpLess_schar_ushort 0.280 ns 0.280 ns 2494961754 |
BM_CmpLess_schar_int 0.221 ns 0.221 ns 3170387379 |
BM_CmpLess_schar_uint 0.232 ns 0.232 ns 3020929987 |
BM_CmpLess_uchar_schar 0.339 ns 0.339 ns 2301816456 |
BM_CmpLess_uchar_uchar 0.221 ns 0.221 ns 3168381752 |
BM_CmpLess_uchar_short 0.221 ns 0.221 ns 3172130050 |
BM_CmpLess_uchar_ushort 0.226 ns 0.226 ns 3177268420 |
BM_CmpLess_uchar_int 0.221 ns 0.221 ns 3171207495 |
BM_CmpLess_uchar_uint 0.221 ns 0.221 ns 3175834030 |
BM_CmpLess_short_schar 0.663 ns 0.663 ns 1047532663 |
BM_CmpLess_short_uchar 0.301 ns 0.301 ns 2210991543 |
BM_CmpLess_short_short 0.220 ns 0.220 ns 3178165314 |
BM_CmpLess_short_ushort 0.280 ns 0.280 ns 2502057388 |
BM_CmpLess_short_int 0.223 ns 0.223 ns 3163647242 |
BM_CmpLess_short_uint 0.233 ns 0.233 ns 3015274874 |
BM_CmpLess_ushort_schar 0.280 ns 0.280 ns 2494740988 |
BM_CmpLess_ushort_uchar 0.316 ns 0.316 ns 2124921107 |
BM_CmpLess_ushort_short 0.280 ns 0.280 ns 2503617728 |
BM_CmpLess_ushort_ushort 0.221 ns 0.221 ns 3166649427 |
BM_CmpLess_ushort_int 0.221 ns 0.221 ns 3109921684 |
BM_CmpLess_ushort_uint 0.221 ns 0.221 ns 3173252017 |
BM_CmpLess_int_schar 0.222 ns 0.222 ns 3164772816 |
BM_CmpLess_int_uchar 0.221 ns 0.221 ns 3141461765 |
BM_CmpLess_int_short 0.221 ns 0.221 ns 3165358991 |
BM_CmpLess_int_ushort 0.221 ns 0.221 ns 3172701309 |
BM_CmpLess_int_int 0.222 ns 0.222 ns 3152883866 |
BM_CmpLess_int_uint 0.232 ns 0.232 ns 2991786154 |
BM_CmpLess_uint_schar 0.221 ns 0.221 ns 3174540447 |
BM_CmpLess_uint_uchar 0.221 ns 0.220 ns 3177358990 |
BM_CmpLess_uint_short 0.300 ns 0.300 ns 2362252788 |
BM_CmpLess_uint_ushort 0.221 ns 0.221 ns 3178372133 |
BM_CmpLess_uint_int 0.221 ns 0.221 ns 3177162541 |
BM_CmpLess_uint_uint 0.221 ns 0.221 ns 3177473622
`----------------------------- .---command stderr------------ |
2025-09-09T20:03:38+08:00 | Running
/home/kisuhorikka/Source/llvm-project/build/libcxx/test/benchmarks/utility/Output/cmp.bench.cpp.dir/t.tmp.exe
| Run on (16 X 2304 MHz CPU s) | CPU Caches: | L1 Data 48 KiB (x8) | L1
Instruction 32 KiB (x8) | L2 Unified 1280 KiB (x8) | L3 Unified 24576 KiB
(x1) | Load Average: 0.12, 0.25, 0.30 `-----------------------------
---
libcxx/include/__utility/cmp.h | 24 ++--
libcxx/test/benchmarks/utility/cmp.bench.cpp | 119 +++++++++++++++++++
2 files changed, 131 insertions(+), 12 deletions(-)
create mode 100644 libcxx/test/benchmarks/utility/cmp.bench.cpp
diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 4c29f09628095..68864e23e0397 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -26,16 +26,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20
+template <typename _Tp, typename _Ip>
+concept __comparison_can_promote_to =
+ sizeof(_Tp) < sizeof(_Ip) || (sizeof(_Tp) == sizeof(_Ip) && __signed_integer<_Tp>);
+
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept {
- if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
- __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ return __t == __u;
+ else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>)
return static_cast<int>(__t) == static_cast<int>(__u);
- } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
- __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>)
return static_cast<long long>(__t) == static_cast<long long>(__u);
- } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
- return __t == __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u;
else
@@ -49,14 +51,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_not_equal(_Tp __t, _Up __u) noexcept {
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_less(_Tp __t, _Up __u) noexcept {
- if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
- __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ return __t < __u;
+ else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>)
return static_cast<int>(__t) < static_cast<int>(__u);
- } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
- __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>)
return static_cast<long long>(__t) < static_cast<long long>(__u);
- } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
- return __t < __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? true : make_unsigned_t<_Tp>(__t) < __u;
else
diff --git a/libcxx/test/benchmarks/utility/cmp.bench.cpp b/libcxx/test/benchmarks/utility/cmp.bench.cpp
new file mode 100644
index 0000000000000..954ab72e37ea6
--- /dev/null
+++ b/libcxx/test/benchmarks/utility/cmp.bench.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <utility>
+#include "../CartesianBenchmarks.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+enum ValueType : size_t {
+ SChar,
+ UChar,
+ Short,
+ UShort,
+ Int,
+ UInt,
+ Long,
+ ULong,
+ LongLong,
+ ULongLong,
+#ifndef TEST_HAS_NO_INT128
+ Int128,
+ UInt128,
+#endif
+};
+
+struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 6> {
+ static constexpr const char* Names[] = {
+ "schar",
+ "uchar",
+ "short",
+ "ushort",
+ "int",
+ "uint",
+ "long",
+ "ulong",
+ "longlong",
+ "ulonglong",
+#ifndef TEST_HAS_NO_INT128
+ "int128",
+ "uint128"
+#endif
+ };
+};
+
+using TestType =
+ std::tuple< signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long
+#ifndef TEST_HAS_NO_INT128
+ ,
+ __int128_t,
+ __uint128_t
+#endif
+ >;
+
+template <typename TType, typename UType>
+struct CmpEqual {
+ static void run(benchmark::State& state) {
+ using T = std::tuple_element_t<TType::value, TestType>;
+ using U = std::tuple_element_t<UType::value, TestType>;
+
+ T x = T{127};
+ U y = U{123};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(x);
+ benchmark::DoNotOptimize(y);
+ benchmark::DoNotOptimize(std::cmp_equal(x, y));
+ }
+ }
+
+ static std::string name() { return "BM_CmpEqual" + TType::name() + UType::name(); }
+};
+
+template <typename TType, typename UType>
+struct CmpLess {
+ static void run(benchmark::State& state) {
+ using T = std::tuple_element_t<TType::value, TestType>;
+ using U = std::tuple_element_t<UType::value, TestType>;
+
+ T x = T{127};
+ U y = U{123};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(x);
+ benchmark::DoNotOptimize(y);
+ benchmark::DoNotOptimize(std::cmp_less(x, y));
+ }
+ }
+
+ static std::string name() { return "BM_CmpLess" + TType::name() + UType::name(); }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ benchmark::Initialize(&argc, argv);
+ if (benchmark::ReportUnrecognizedArguments(argc, argv))
+ return 1;
+
+ makeCartesianProductBenchmark<CmpEqual, AllValueTypes, AllValueTypes>();
+ makeCartesianProductBenchmark<CmpLess, AllValueTypes, AllValueTypes>();
+ benchmark::RunSpecifiedBenchmarks();
+
+ return 0;
+}
More information about the libcxx-commits
mailing list