[libcxx-commits] [libcxx] [libcxx] Optimize `ranges::fold_left_with_iter` for segmented iterators (PR #177853)

Connector Switch via libcxx-commits libcxx-commits at lists.llvm.org
Thu Feb 5 03:01:19 PST 2026


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/177853

>From 50f1bd8d398bd8e24ff2f346ba538d38afde59cc Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 25 Jan 2026 20:37:09 +0800
Subject: [PATCH 1/5] optimize fold_left_with_iter

---
 libcxx/docs/ReleaseNotes/23.rst          |  4 ++++
 libcxx/include/__algorithm/ranges_fold.h | 18 ++++++++++++------
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 73f5984768592..046ce4ad22b51 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -41,6 +41,10 @@ Implemented Papers
 Improvements and New Features
 -----------------------------
 
+- The ``std::ranges::fold_left_with_iter`` algorithm has been optimized for
+  segmented iterators, resulting in a performance improvement for
+  ``std::deque<int>`` iterators.
+
 Deprecations and Removals
 -------------------------
 
diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index d2c3921398504..82614ec311164 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -10,12 +10,14 @@
 #ifndef _LIBCPP___ALGORITHM_RANGES_FOLD_H
 #define _LIBCPP___ALGORITHM_RANGES_FOLD_H
 
+#include <__algorithm/for_each.h>
 #include <__concepts/assignable.h>
 #include <__concepts/constructible.h>
 #include <__concepts/convertible_to.h>
 #include <__concepts/invocable.h>
 #include <__concepts/movable.h>
 #include <__config>
+#include <__functional/identity.h>
 #include <__functional/invoke.h>
 #include <__functional/reference_wrapper.h>
 #include <__iterator/concepts.h>
@@ -80,18 +82,22 @@ concept __indirectly_binary_left_foldable =
 struct __fold_left_with_iter {
   template <input_iterator _Ip, sentinel_for<_Ip> _Sp, class _Tp, __indirectly_binary_left_foldable<_Tp, _Ip> _Fp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Tp __init, _Fp __f) {
-    using _Up = decay_t<invoke_result_t<_Fp&, _Tp, iter_reference_t<_Ip>>>;
+    using _Up        = decay_t<invoke_result_t<_Fp&, _Tp, iter_reference_t<_Ip>>>;
+    using __iter_ref = decltype(*__first);
 
     if (__first == __last) {
       return fold_left_with_iter_result<_Ip, _Up>{std::move(__first), _Up(std::move(__init))};
     }
 
     _Up __result = std::invoke(__f, std::move(__init), *__first);
-    for (++__first; __first != __last; ++__first) {
-      __result = std::invoke(__f, std::move(__result), *__first);
-    }
-
-    return fold_left_with_iter_result<_Ip, _Up>{std::move(__first), std::move(__result)};
+    ++__first;
+    auto __for_each_f = [&](__iter_ref __element) {
+      __result = std::invoke(__f, std::move(__result), std::forward<__iter_ref>(__element));
+    };
+    __identity __proj;
+    auto __end = std::__for_each(std::move(__first), std::move(__last), __for_each_f, __proj);
+
+    return fold_left_with_iter_result<_Ip, _Up>{std::move(__end), std::move(__result)};
   }
 
   template <input_range _Rp, class _Tp, __indirectly_binary_left_foldable<_Tp, iterator_t<_Rp>> _Fp>

>From 8fb997795da2e9e00ae940f57b7851e5977c21e7 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 2 Feb 2026 23:20:55 +0800
Subject: [PATCH 2/5] mention improvement in release notes

---
 libcxx/docs/ReleaseNotes/23.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 046ce4ad22b51..4a23ecf975aea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -42,8 +42,8 @@ Improvements and New Features
 -----------------------------
 
 - The ``std::ranges::fold_left_with_iter`` algorithm has been optimized for
-  segmented iterators, resulting in a performance improvement for
-  ``std::deque<int>`` iterators.
+  segmented iterators, resulting in a performance improvement of up to 1.38x
+  for ``std::deque<int>`` iterators.
 
 Deprecations and Removals
 -------------------------

>From ef6d421759ccda87ae87063ba04f188e29fc0eb2 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Wed, 4 Feb 2026 23:29:02 +0800
Subject: [PATCH 3/5] remove for_each_f; add test coverage

---
 libcxx/include/__algorithm/ranges_fold.h            | 11 +++++++----
 .../alg.nonmodifying/alg.fold/left_folds.pass.cpp   | 13 +++++++++++++
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index 82614ec311164..b99fe858efd5c 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -91,11 +91,14 @@ struct __fold_left_with_iter {
 
     _Up __result = std::invoke(__f, std::move(__init), *__first);
     ++__first;
-    auto __for_each_f = [&](__iter_ref __element) {
-      __result = std::invoke(__f, std::move(__result), std::forward<__iter_ref>(__element));
-    };
     __identity __proj;
-    auto __end = std::__for_each(std::move(__first), std::move(__last), __for_each_f, __proj);
+    auto __end = std::__for_each(
+        std::move(__first),
+        std::move(__last),
+        [&](__iter_ref __element) {
+          __result = std::invoke(__f, std::move(__result), std::forward<__iter_ref>(__element));
+        },
+        __proj);
 
     return fold_left_with_iter_result<_Ip, _Up>{std::move(__end), std::move(__result)};
   }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
index 4987ca9cac4ae..cd1db8d63b12c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
@@ -36,6 +36,7 @@
 #include <iterator>
 #include <list>
 #include <ranges>
+#include <set>
 #include <string_view>
 #include <string>
 #include <vector>
@@ -322,6 +323,18 @@ void runtime_only_test_case() {
                                  // int(-15.5) + -6.6 = -15 + -6.6 = -21.6.
     check(data, 0.0, plus, expected);
   }
+
+  {
+    auto const data     = std::set<double>{-1.1, -2.2, -3.3, -4.4, -5.5, -6.6};
+    auto plus           = [](int const x, double const y) { return x + y; };
+    auto const expected = -21.1; // int(  0.0) + -6.6 =   0 + -6.6 =  -6.6
+                                 // int(- 6.6) + -5.5 = - 6 + -5.5 = -11.5
+                                 // int(-11.5) + -4.4 = -11 + -4.4 = -15.4
+                                 // int(-15.4) + -3.3 = -15 + -3.3 = -18.3
+                                 // int(-18.3) + -2.2 = -18 + -2.2 = -20.2
+                                 // int(-20.2) + -1.1 = -20 + -1.1 = -21.1.
+    check(data, 0.0, plus, expected);
+  }
 }
 
 int main(int, char**) {

>From a9dbf70511c51915454a758b0c971a98aef310bd Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Thu, 5 Feb 2026 08:37:31 +0800
Subject: [PATCH 4/5] simpler test

---
 .../alg.nonmodifying/alg.fold/left_folds.pass.cpp    | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
index cd1db8d63b12c..107e09a63c96f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
@@ -325,15 +325,9 @@ void runtime_only_test_case() {
   }
 
   {
-    auto const data     = std::set<double>{-1.1, -2.2, -3.3, -4.4, -5.5, -6.6};
-    auto plus           = [](int const x, double const y) { return x + y; };
-    auto const expected = -21.1; // int(  0.0) + -6.6 =   0 + -6.6 =  -6.6
-                                 // int(- 6.6) + -5.5 = - 6 + -5.5 = -11.5
-                                 // int(-11.5) + -4.4 = -11 + -4.4 = -15.4
-                                 // int(-15.4) + -3.3 = -15 + -3.3 = -18.3
-                                 // int(-18.3) + -2.2 = -18 + -2.2 = -20.2
-                                 // int(-20.2) + -1.1 = -20 + -1.1 = -21.1.
-    check(data, 0.0, plus, expected);
+    auto const data     = std::set<int>{2, 4, 6, 8, 10, 12};
+    auto const expected = triangular_sum(data);
+    check(data, 0, std::plus<long>(), static_cast<long>(expected));
   }
 }
 

>From d9a719fb3323bd7c70c712ffffd2e00db2bf5807 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Thu, 5 Feb 2026 19:01:04 +0800
Subject: [PATCH 5/5] use auto&&

---
 libcxx/include/__algorithm/ranges_fold.h | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index b99fe858efd5c..c2d150a16d328 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -82,8 +82,7 @@ concept __indirectly_binary_left_foldable =
 struct __fold_left_with_iter {
   template <input_iterator _Ip, sentinel_for<_Ip> _Sp, class _Tp, __indirectly_binary_left_foldable<_Tp, _Ip> _Fp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Tp __init, _Fp __f) {
-    using _Up        = decay_t<invoke_result_t<_Fp&, _Tp, iter_reference_t<_Ip>>>;
-    using __iter_ref = decltype(*__first);
+    using _Up = decay_t<invoke_result_t<_Fp&, _Tp, iter_reference_t<_Ip>>>;
 
     if (__first == __last) {
       return fold_left_with_iter_result<_Ip, _Up>{std::move(__first), _Up(std::move(__init))};
@@ -95,8 +94,8 @@ struct __fold_left_with_iter {
     auto __end = std::__for_each(
         std::move(__first),
         std::move(__last),
-        [&](__iter_ref __element) {
-          __result = std::invoke(__f, std::move(__result), std::forward<__iter_ref>(__element));
+        [&](auto&& __element) {
+          __result = std::invoke(__f, std::move(__result), std::forward<decltype(__element)>(__element));
         },
         __proj);
 



More information about the libcxx-commits mailing list