[libcxx-commits] [libcxx] [libc++][ranges] optimize the performance of `ranges::starts_with` (PR #84570)

Xiaoyang Liu via libcxx-commits libcxx-commits at lists.llvm.org
Mon Apr 8 01:06:22 PDT 2024


https://github.com/xiaoyang-sde updated https://github.com/llvm/llvm-project/pull/84570

>From 3a384218365b175e8884a91125efe4e07f9e500a Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 8 Mar 2024 13:43:39 -0800
Subject: [PATCH 01/14] [libc++][ranges] detect size mismatch in
 'ranges::starts_with'

---
 .../include/__algorithm/ranges_starts_with.h  | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index 90e184aa9bccc2..b39e1657a855ea 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -15,6 +15,7 @@
 #include <__functional/identity.h>
 #include <__functional/ranges_operations.h>
 #include <__iterator/concepts.h>
+#include <__iterator/distance.h>
 #include <__iterator/indirectly_comparable.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
@@ -50,6 +51,17 @@ struct __fn {
       _Pred __pred   = {},
       _Proj1 __proj1 = {},
       _Proj2 __proj2 = {}) const {
+    if constexpr (sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>) {
+      auto __n1 = ranges::distance(__first1, __last1);
+      auto __n2 = ranges::distance(__first2, __last2);
+      if (__n2 == 0) {
+        return true;
+      }
+      if (__n2 > __n1) {
+        return false;
+      }
+    }
+
     return __mismatch::__fn::__go(
                std::move(__first1),
                std::move(__last1),
@@ -69,6 +81,17 @@ struct __fn {
     requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, _Pred, _Proj1, _Proj2>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
       _Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
+    if constexpr (sized_range<_Range1> && sized_range<_Range2>) {
+      auto __n1 = ranges::size(__range1);
+      auto __n2 = ranges::size(__range2);
+      if (__n2 == 0) {
+        return true;
+      }
+      if (__n2 > __n1) {
+        return false;
+      }
+    }
+
     return __mismatch::__fn::__go(
                ranges::begin(__range1),
                ranges::end(__range1),

>From 5f8773ea9dcedb1a7fbaf02fd7b145834c1b3cf3 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 8 Mar 2024 13:44:51 -0800
Subject: [PATCH 02/14] [libc++][ranges] change the 'operator()' of
 'ranges::starts_with' to 'static'

---
 libcxx/include/__algorithm/ranges_starts_with.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index b39e1657a855ea..93409d5b109137 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -43,14 +43,14 @@ struct __fn {
             class _Proj1 = identity,
             class _Proj2 = identity>
     requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
-  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool operator()(
       _Iter1 __first1,
       _Sent1 __last1,
       _Iter2 __first2,
       _Sent2 __last2,
       _Pred __pred   = {},
       _Proj1 __proj1 = {},
-      _Proj2 __proj2 = {}) const {
+      _Proj2 __proj2 = {}) {
     if constexpr (sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>) {
       auto __n1 = ranges::distance(__first1, __last1);
       auto __n2 = ranges::distance(__first2, __last2);
@@ -79,8 +79,8 @@ struct __fn {
             class _Proj1 = identity,
             class _Proj2 = identity>
     requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, _Pred, _Proj1, _Proj2>
-  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
-      _Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool
+  operator()(_Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) {
     if constexpr (sized_range<_Range1> && sized_range<_Range2>) {
       auto __n1 = ranges::size(__range1);
       auto __n2 = ranges::size(__range2);

>From 66fb3d8ae037ec041fa765650c71359d295b0ee8 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 8 Mar 2024 16:05:28 -0800
Subject: [PATCH 03/14] [libc++][ranges] dispatch to 'ranges::equal' in
 'ranges::starts_with'

---
 .../include/__algorithm/ranges_starts_with.h  | 87 +++++++++++--------
 1 file changed, 50 insertions(+), 37 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index 93409d5b109137..ebcc15555f5544 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -9,11 +9,12 @@
 #ifndef _LIBCPP___ALGORITHM_RANGES_STARTS_WITH_H
 #define _LIBCPP___ALGORITHM_RANGES_STARTS_WITH_H
 
-#include <__algorithm/in_in_result.h>
+#include <__algorithm/ranges_equal.h>
 #include <__algorithm/ranges_mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/ranges_operations.h>
+#include <__functional/reference_wrapper.h>
 #include <__iterator/concepts.h>
 #include <__iterator/distance.h>
 #include <__iterator/indirectly_comparable.h>
@@ -35,22 +36,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 namespace ranges {
 namespace __starts_with {
 struct __fn {
-  template <input_iterator _Iter1,
-            sentinel_for<_Iter1> _Sent1,
-            input_iterator _Iter2,
-            sentinel_for<_Iter2> _Sent2,
-            class _Pred  = ranges::equal_to,
-            class _Proj1 = identity,
-            class _Proj2 = identity>
-    requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
-  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool operator()(
+  template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Pred, class _Proj1, class _Proj2>
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool __starts_with_impl(
       _Iter1 __first1,
       _Sent1 __last1,
       _Iter2 __first2,
       _Sent2 __last2,
-      _Pred __pred   = {},
-      _Proj1 __proj1 = {},
-      _Proj2 __proj2 = {}) {
+      _Pred& __pred,
+      _Proj1& __proj1,
+      _Proj2& __proj2) {
     if constexpr (sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>) {
       auto __n1 = ranges::distance(__first1, __last1);
       auto __n2 = ranges::distance(__first2, __last2);
@@ -60,19 +54,50 @@ struct __fn {
       if (__n2 > __n1) {
         return false;
       }
+
+      if constexpr (random_access_iterator<_Iter1> && random_access_iterator<_Iter2>) {
+        return ranges::equal(
+            std::move(__first1),
+            ranges::next(__first1, __n2),
+            std::move(__first2),
+            std::move(__last2),
+            std::ref(__pred),
+            std::ref(__proj1),
+            std::ref(__proj2));
+      }
     }
 
-    return __mismatch::__fn::__go(
+    return ranges::mismatch(
                std::move(__first1),
                std::move(__last1),
                std::move(__first2),
                std::move(__last2),
-               __pred,
-               __proj1,
-               __proj2)
+               std::ref(__pred),
+               std::ref(__proj1),
+               std::ref(__proj2))
                .in2 == __last2;
   }
 
+  template <input_iterator _Iter1,
+            sentinel_for<_Iter1> _Sent1,
+            input_iterator _Iter2,
+            sentinel_for<_Iter2> _Sent2,
+            class _Pred  = ranges::equal_to,
+            class _Proj1 = identity,
+            class _Proj2 = identity>
+    requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool operator()(
+      _Iter1 __first1,
+      _Sent1 __last1,
+      _Iter2 __first2,
+      _Sent2 __last2,
+      _Pred __pred   = {},
+      _Proj1 __proj1 = {},
+      _Proj2 __proj2 = {}) {
+    return __starts_with_impl(
+        std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), __pred, __proj1, __proj2);
+  }
+
   template <input_range _Range1,
             input_range _Range2,
             class _Pred  = ranges::equal_to,
@@ -81,26 +106,14 @@ struct __fn {
     requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, _Pred, _Proj1, _Proj2>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool
   operator()(_Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) {
-    if constexpr (sized_range<_Range1> && sized_range<_Range2>) {
-      auto __n1 = ranges::size(__range1);
-      auto __n2 = ranges::size(__range2);
-      if (__n2 == 0) {
-        return true;
-      }
-      if (__n2 > __n1) {
-        return false;
-      }
-    }
-
-    return __mismatch::__fn::__go(
-               ranges::begin(__range1),
-               ranges::end(__range1),
-               ranges::begin(__range2),
-               ranges::end(__range2),
-               __pred,
-               __proj1,
-               __proj2)
-               .in2 == ranges::end(__range2);
+    return __starts_with_impl(
+        ranges::begin(__range1),
+        ranges::end(__range1),
+        ranges::begin(__range2),
+        ranges::end(__range2),
+        __pred,
+        __proj1,
+        __proj2);
   }
 };
 } // namespace __starts_with

>From 0ab89c199570f32a44a2a5ea2eb6f44ec0388939 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 8 Mar 2024 23:38:02 -0800
Subject: [PATCH 04/14] [libc++][ranges] add template specialization for
 '__desugars_to'

---
 libcxx/include/__functional/operations.h        | 9 +++++++++
 libcxx/include/__functional/ranges_operations.h | 5 +++++
 2 files changed, 14 insertions(+)

diff --git a/libcxx/include/__functional/operations.h b/libcxx/include/__functional/operations.h
index 7ddc00650f162f..4565e1415a6e02 100644
--- a/libcxx/include/__functional/operations.h
+++ b/libcxx/include/__functional/operations.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__functional/binary_function.h>
 #include <__functional/unary_function.h>
+#include <__fwd/functional.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/operation_traits.h>
 #include <__utility/forward.h>
@@ -316,10 +317,18 @@ struct _LIBCPP_TEMPLATE_VIS equal_to<void> {
 // comparison when we don't perform an implicit conversion when calling it.
 template <class _Tp>
 struct __desugars_to<__equal_tag, equal_to<_Tp>, _Tp, _Tp> : true_type {};
+template <class _Tp>
+struct __desugars_to<__equal_tag, reference_wrapper<equal_to<_Tp>>, _Tp, _Tp> : true_type {};
+template <class _Tp>
+struct __desugars_to<__equal_tag, reference_wrapper<const equal_to<_Tp>>, _Tp, _Tp> : true_type {};
 
 // In the transparent case, we do not enforce that
 template <class _Tp, class _Up>
 struct __desugars_to<__equal_tag, equal_to<void>, _Tp, _Up> : true_type {};
+template <class _Tp, class _Up>
+struct __desugars_to<__equal_tag, reference_wrapper<equal_to<void>>, _Tp, _Up> : true_type {};
+template <class _Tp, class _Up>
+struct __desugars_to<__equal_tag, reference_wrapper<const equal_to<void>>, _Tp, _Up> : true_type {};
 
 #if _LIBCPP_STD_VER >= 14
 template <class _Tp = void>
diff --git a/libcxx/include/__functional/ranges_operations.h b/libcxx/include/__functional/ranges_operations.h
index 38b28018049eb7..be400c42a65790 100644
--- a/libcxx/include/__functional/ranges_operations.h
+++ b/libcxx/include/__functional/ranges_operations.h
@@ -13,6 +13,7 @@
 #include <__concepts/equality_comparable.h>
 #include <__concepts/totally_ordered.h>
 #include <__config>
+#include <__fwd/functional.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/operation_traits.h>
 #include <__utility/forward.h>
@@ -99,6 +100,10 @@ struct greater_equal {
 // operator are of the same type
 template <class _Tp, class _Up>
 struct __desugars_to<__equal_tag, ranges::equal_to, _Tp, _Up> : true_type {};
+template <class _Tp, class _Up>
+struct __desugars_to<__equal_tag, reference_wrapper<ranges::equal_to>, _Tp, _Up> : true_type {};
+template <class _Tp, class _Up>
+struct __desugars_to<__equal_tag, reference_wrapper<const ranges::equal_to>, _Tp, _Up> : true_type {};
 
 #endif // _LIBCPP_STD_VER >= 20
 

>From 919f17a7d077ff996f9b5419d8d6da349ce9cddb Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 8 Mar 2024 23:40:08 -0800
Subject: [PATCH 05/14] [libc++][ranges] add benchmark for
 'ranges::starts_with'

---
 libcxx/benchmarks/CMakeLists.txt              |   1 +
 .../algorithms/ranges_starts_with.bench.cpp   | 107 ++++++++++++++++++
 2 files changed, 108 insertions(+)
 create mode 100644 libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp

diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt
index b436e96f178b70..621b295a92c8d2 100644
--- a/libcxx/benchmarks/CMakeLists.txt
+++ b/libcxx/benchmarks/CMakeLists.txt
@@ -186,6 +186,7 @@ set(BENCHMARK_TESTS
     algorithms/pstl.stable_sort.bench.cpp
     algorithms/push_heap.bench.cpp
     algorithms/ranges_contains.bench.cpp
+    algorithms/ranges_starts_with.bench.cpp
     algorithms/ranges_ends_with.bench.cpp
     algorithms/ranges_make_heap.bench.cpp
     algorithms/ranges_make_heap_then_sort_heap.bench.cpp
diff --git a/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp b/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
new file mode 100644
index 00000000000000..9986fdb1684fc7
--- /dev/null
+++ b/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <benchmark/benchmark.h>
+#include <functional>
+#include <vector>
+
+#include "test_iterators.h"
+
+static void bm_starts_with_contiguous_iter_with_memcmp_optimization(benchmark::State& state) {
+  std::vector<int> a(state.range(), 1);
+  std::vector<int> p(state.range(), 1);
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(a);
+    benchmark::DoNotOptimize(p);
+
+    auto begin1 = contiguous_iterator(a.data());
+    auto end1   = contiguous_iterator(a.data() + a.size());
+    auto begin2 = contiguous_iterator(p.data());
+    auto end2   = contiguous_iterator(p.data() + p.size());
+
+    benchmark::DoNotOptimize(std::ranges::starts_with(begin1, end1, begin2, end2));
+  }
+}
+BENCHMARK(bm_starts_with_contiguous_iter_with_memcmp_optimization)->RangeMultiplier(16)->Range(16, 16 << 20);
+
+static void bm_starts_with_contiguous_iter(benchmark::State& state) {
+  std::vector<int> a(state.range(), 1);
+  std::vector<int> p(state.range(), 1);
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(a);
+    benchmark::DoNotOptimize(p);
+
+    auto begin1 = contiguous_iterator(a.data());
+    auto end1   = contiguous_iterator(a.data() + a.size());
+    auto begin2 = contiguous_iterator(p.data());
+    auto end2   = contiguous_iterator(p.data() + p.size());
+
+    benchmark::DoNotOptimize(
+        std::ranges::starts_with(begin1, end1, begin2, end2, [](const int a, const int b) { return a == b; }));
+  }
+}
+BENCHMARK(bm_starts_with_contiguous_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
+
+static void bm_starts_with_random_access_iter(benchmark::State& state) {
+  std::vector<int> a(state.range(), 1);
+  std::vector<int> p(state.range(), 1);
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(a);
+    benchmark::DoNotOptimize(p);
+
+    auto begin1 = random_access_iterator(a.data());
+    auto end1   = random_access_iterator(a.data() + a.size());
+    auto begin2 = random_access_iterator(p.data());
+    auto end2   = random_access_iterator(p.data() + p.size());
+
+    benchmark::DoNotOptimize(std::ranges::starts_with(begin1, end1, begin2, end2));
+  }
+}
+BENCHMARK(bm_starts_with_random_access_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
+
+static void bm_starts_with_bidirectional_iter(benchmark::State& state) {
+  std::vector<int> a(state.range(), 1);
+  std::vector<int> p(state.range(), 1);
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(a);
+    benchmark::DoNotOptimize(p);
+
+    auto begin1 = bidirectional_iterator(a.data());
+    auto end1   = bidirectional_iterator(a.data() + a.size());
+    auto begin2 = bidirectional_iterator(p.data());
+    auto end2   = bidirectional_iterator(p.data() + p.size());
+
+    benchmark::DoNotOptimize(std::ranges::starts_with(begin1, end1, begin2, end2));
+  }
+}
+BENCHMARK(bm_starts_with_bidirectional_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
+
+static void bm_starts_with_forward_iter(benchmark::State& state) {
+  std::vector<int> a(state.range(), 1);
+  std::vector<int> p(state.range(), 1);
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(a);
+    benchmark::DoNotOptimize(p);
+
+    auto begin1 = forward_iterator(a.data());
+    auto end1   = forward_iterator(a.data() + a.size());
+    auto begin2 = forward_iterator(p.data());
+    auto end2   = forward_iterator(p.data() + p.size());
+
+    benchmark::DoNotOptimize(std::ranges::starts_with(begin1, end1, begin2, end2));
+  }
+}
+BENCHMARK(bm_starts_with_forward_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
+
+BENCHMARK_MAIN();

>From 9611b1bfdddaa01dc04035ecbfbf8b4fcf8aabb5 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 9 Mar 2024 11:33:01 -0800
Subject: [PATCH 06/14] [libc++][ranges] add missing headers to
 'ranges_starts_with.h'

---
 libcxx/include/__algorithm/ranges_starts_with.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index ebcc15555f5544..14575fec793ba5 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -18,6 +18,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/distance.h>
 #include <__iterator/indirectly_comparable.h>
+#include <__iterator/next.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__utility/move.h>

>From a8a5922f148a0cb45b879cbb507750bc10818ecf Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 9 Mar 2024 11:33:16 -0800
Subject: [PATCH 07/14] [libc++][ranges] remove redundant headers from
 'ranges_starts_with.bench.cpp'

---
 libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp b/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
index 9986fdb1684fc7..a4a5fcf875d4e2 100644
--- a/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
+++ b/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 #include <benchmark/benchmark.h>
-#include <functional>
 #include <vector>
 
 #include "test_iterators.h"

>From fd76420c816d502efad2e141c29c0bc4d4754cf1 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 9 Mar 2024 15:55:25 -0800
Subject: [PATCH 08/14] [libc++][ranges] add a space between consecutive right
 angle brackets

---
 libcxx/include/__functional/operations.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__functional/operations.h b/libcxx/include/__functional/operations.h
index 4565e1415a6e02..6c617f9890c501 100644
--- a/libcxx/include/__functional/operations.h
+++ b/libcxx/include/__functional/operations.h
@@ -318,17 +318,17 @@ struct _LIBCPP_TEMPLATE_VIS equal_to<void> {
 template <class _Tp>
 struct __desugars_to<__equal_tag, equal_to<_Tp>, _Tp, _Tp> : true_type {};
 template <class _Tp>
-struct __desugars_to<__equal_tag, reference_wrapper<equal_to<_Tp>>, _Tp, _Tp> : true_type {};
+struct __desugars_to<__equal_tag, reference_wrapper<equal_to<_Tp> >, _Tp, _Tp> : true_type {};
 template <class _Tp>
-struct __desugars_to<__equal_tag, reference_wrapper<const equal_to<_Tp>>, _Tp, _Tp> : true_type {};
+struct __desugars_to<__equal_tag, reference_wrapper<const equal_to<_Tp> >, _Tp, _Tp> : true_type {};
 
 // In the transparent case, we do not enforce that
 template <class _Tp, class _Up>
 struct __desugars_to<__equal_tag, equal_to<void>, _Tp, _Up> : true_type {};
 template <class _Tp, class _Up>
-struct __desugars_to<__equal_tag, reference_wrapper<equal_to<void>>, _Tp, _Up> : true_type {};
+struct __desugars_to<__equal_tag, reference_wrapper<equal_to<void> >, _Tp, _Up> : true_type {};
 template <class _Tp, class _Up>
-struct __desugars_to<__equal_tag, reference_wrapper<const equal_to<void>>, _Tp, _Up> : true_type {};
+struct __desugars_to<__equal_tag, reference_wrapper<const equal_to<void> >, _Tp, _Up> : true_type {};
 
 #if _LIBCPP_STD_VER >= 14
 template <class _Tp = void>

>From 9cb885e5d11dcd17b2544618dfbc6f671c8c5e34 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Tue, 12 Mar 2024 12:08:35 -0700
Subject: [PATCH 09/14] [libc++][ranges] add a blank line between namespaces

---
 libcxx/include/__algorithm/ranges_starts_with.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index 14575fec793ba5..85ddfb1e5518d9 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -118,6 +118,7 @@ struct __fn {
   }
 };
 } // namespace __starts_with
+
 inline namespace __cpo {
 inline constexpr auto starts_with = __starts_with::__fn{};
 } // namespace __cpo

>From abfb36b0b8b958d0c6e63f73bd242e27e1d11a80 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 16 Mar 2024 23:37:44 -0700
Subject: [PATCH 10/14] [libc++][ranges] optimize 'ranges::starts_with' for
 'sized_range'

---
 .../include/__algorithm/ranges_starts_with.h  | 79 +++++++++++--------
 1 file changed, 45 insertions(+), 34 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index 85ddfb1e5518d9..1e13746c8bcf9e 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -21,6 +21,7 @@
 #include <__iterator/next.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
+#include <__ranges/size.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -37,15 +38,22 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 namespace ranges {
 namespace __starts_with {
 struct __fn {
-  template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Pred, class _Proj1, class _Proj2>
-  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool __starts_with_impl(
+  template <input_iterator _Iter1,
+            sentinel_for<_Iter1> _Sent1,
+            input_iterator _Iter2,
+            sentinel_for<_Iter2> _Sent2,
+            class _Pred  = ranges::equal_to,
+            class _Proj1 = identity,
+            class _Proj2 = identity>
+    requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool operator()(
       _Iter1 __first1,
       _Sent1 __last1,
       _Iter2 __first2,
       _Sent2 __last2,
-      _Pred& __pred,
-      _Proj1& __proj1,
-      _Proj2& __proj2) {
+      _Pred __pred   = {},
+      _Proj1 __proj1 = {},
+      _Proj2 __proj2 = {}) {
     if constexpr (sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>) {
       auto __n1 = ranges::distance(__first1, __last1);
       auto __n2 = ranges::distance(__first2, __last2);
@@ -56,7 +64,7 @@ struct __fn {
         return false;
       }
 
-      if constexpr (random_access_iterator<_Iter1> && random_access_iterator<_Iter2>) {
+      if constexpr (contiguous_iterator<_Iter1> && contiguous_iterator<_Iter2>) {
         return ranges::equal(
             std::move(__first1),
             ranges::next(__first1, __n2),
@@ -79,26 +87,6 @@ struct __fn {
                .in2 == __last2;
   }
 
-  template <input_iterator _Iter1,
-            sentinel_for<_Iter1> _Sent1,
-            input_iterator _Iter2,
-            sentinel_for<_Iter2> _Sent2,
-            class _Pred  = ranges::equal_to,
-            class _Proj1 = identity,
-            class _Proj2 = identity>
-    requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
-  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool operator()(
-      _Iter1 __first1,
-      _Sent1 __last1,
-      _Iter2 __first2,
-      _Sent2 __last2,
-      _Pred __pred   = {},
-      _Proj1 __proj1 = {},
-      _Proj2 __proj2 = {}) {
-    return __starts_with_impl(
-        std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), __pred, __proj1, __proj2);
-  }
-
   template <input_range _Range1,
             input_range _Range2,
             class _Pred  = ranges::equal_to,
@@ -107,14 +95,37 @@ struct __fn {
     requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, _Pred, _Proj1, _Proj2>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool
   operator()(_Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) {
-    return __starts_with_impl(
-        ranges::begin(__range1),
-        ranges::end(__range1),
-        ranges::begin(__range2),
-        ranges::end(__range2),
-        __pred,
-        __proj1,
-        __proj2);
+    if constexpr (sized_range<_Range1> && sized_range<_Range2>) {
+      auto __n1 = ranges::size(__range1);
+      auto __n2 = ranges::size(__range2);
+      if (__n2 == 0) {
+        return true;
+      }
+      if (__n2 > __n1) {
+        return false;
+      }
+
+      if constexpr (contiguous_range<_Range1> && contiguous_range<_Range2>) {
+        return ranges::equal(
+            ranges::begin(__range1),
+            ranges::next(ranges::begin(__range1), __n2),
+            ranges::begin(__range2),
+            ranges::end(__range2),
+            std::ref(__pred),
+            std::ref(__proj1),
+            std::ref(__proj2));
+      }
+    }
+
+    return ranges::mismatch(
+               ranges::begin(__range1),
+               ranges::end(__range1),
+               ranges::begin(__range2),
+               ranges::end(__range2),
+               std::ref(__pred),
+               std::ref(__proj1),
+               std::ref(__proj2))
+               .in2 == ranges::end(__range2);
   }
 };
 } // namespace __starts_with

>From 82b7a80b004bd02213ec699cbc50bf69959db3e1 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 16 Mar 2024 23:50:32 -0700
Subject: [PATCH 11/14] [libc++][ranges] fix a use-after-move bug

---
 libcxx/include/__algorithm/ranges_starts_with.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index 1e13746c8bcf9e..2ab84bf39f447e 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -80,7 +80,7 @@ struct __fn {
                std::move(__first1),
                std::move(__last1),
                std::move(__first2),
-               std::move(__last2),
+               __last2,
                std::ref(__pred),
                std::ref(__proj1),
                std::ref(__proj2))

>From a946210da359824203492d1d0eabb720a16eeda8 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 20 Mar 2024 21:08:05 -0700
Subject: [PATCH 12/14] [libc++][ranges] change the 'operator()' of
 'ranges::starts_with' to 'static'

---
 libcxx/include/__algorithm/ranges_starts_with.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index 2ab84bf39f447e..54a86780749fa1 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -46,14 +46,14 @@ struct __fn {
             class _Proj1 = identity,
             class _Proj2 = identity>
     requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
-  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool operator()(
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
       _Iter1 __first1,
       _Sent1 __last1,
       _Iter2 __first2,
       _Sent2 __last2,
       _Pred __pred   = {},
       _Proj1 __proj1 = {},
-      _Proj2 __proj2 = {}) {
+      _Proj2 __proj2 = {}) const {
     if constexpr (sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>) {
       auto __n1 = ranges::distance(__first1, __last1);
       auto __n2 = ranges::distance(__first2, __last2);

>From 80a6c357e0b1446036ec9d95f2448aefe31dbf0e Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 20 Mar 2024 21:08:58 -0700
Subject: [PATCH 13/14] [libc++][ranges] change the 'operator()' of
 'ranges::starts_with' to 'static'

---
 libcxx/include/__algorithm/ranges_starts_with.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_starts_with.h b/libcxx/include/__algorithm/ranges_starts_with.h
index 54a86780749fa1..72e4d14f4f8cce 100644
--- a/libcxx/include/__algorithm/ranges_starts_with.h
+++ b/libcxx/include/__algorithm/ranges_starts_with.h
@@ -93,8 +93,8 @@ struct __fn {
             class _Proj1 = identity,
             class _Proj2 = identity>
     requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, _Pred, _Proj1, _Proj2>
-  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr bool
-  operator()(_Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) {
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
+      _Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
     if constexpr (sized_range<_Range1> && sized_range<_Range2>) {
       auto __n1 = ranges::size(__range1);
       auto __n2 = ranges::size(__range2);

>From 38664151c44c9b5ea36750598b613d5e6814eb0c Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Mon, 8 Apr 2024 01:06:02 -0700
Subject: [PATCH 14/14] [libc++][ranges] optimize the performance of
 'ranges::starts_with'

---
 libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp b/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
index a4a5fcf875d4e2..1c3ac6d02ce140 100644
--- a/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
+++ b/libcxx/benchmarks/algorithms/ranges_starts_with.bench.cpp
@@ -43,6 +43,7 @@ static void bm_starts_with_contiguous_iter(benchmark::State& state) {
     auto begin2 = contiguous_iterator(p.data());
     auto end2   = contiguous_iterator(p.data() + p.size());
 
+    // Using a custom predicate to make sure the memcmp optimization doesn't get invoked
     benchmark::DoNotOptimize(
         std::ranges::starts_with(begin1, end1, begin2, end2, [](const int a, const int b) { return a == b; }));
   }



More information about the libcxx-commits mailing list