[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