[PATCH] libstdc++: optimize ranges::for_each for segmented iterators

Yuao Ma <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel
Message-ID <CA+m0iR_1hVY8LmCwLT6jJ_e9mKnyX_LOnixZunSxFqt6Oz2Fmg@mail.gmail.com>
Hi!

Similar to std::for_each, this patch optimizes ranges::for_each for
segmented iterators.

Fully tested on x86_64-linux with no regressions.

Using the newly added benchmark, it shows a 3x improvement when using
ranges::for_each with std::deque.

=== Wed Aug 19 03:28:22 PM UTC 2026 ===
for_each.cc               std::for_each vector<int>   2r    1u    0s
      0mem    0pf
for_each.cc               std::for_each deque<int>   2r    2u    0s
     0mem    0pf
for_each.cc               std::for_each list<int>    13r   14u    0s
      0mem    0pf
for_each.cc               std::ranges::for_each vector<int>   2r    2u
   0s         0mem    0pf
for_each.cc               std::ranges::for_each deque<int>   6r    5u
  0s         0mem    0pf
for_each.cc               std::ranges::for_each list<int>  13r   14u
 0s         0mem    0pf
=== Wed Aug 19 04:09:51 PM UTC 2026 ===
for_each.cc               std::for_each vector<int>   2r    1u    0s
      0mem    0pf
for_each.cc               std::for_each deque<int>   2r    2u    0s
     0mem    0pf
for_each.cc               std::for_each list<int>    13r   14u    0s
      0mem    0pf
for_each.cc               std::ranges::for_each vector<int>   2r    2u
   0s         0mem    0pf
for_each.cc               std::ranges::for_each deque<int>   2r    1u
  0s         0mem    0pf
for_each.cc               std::ranges::for_each list<int>  13r   14u
 0s         0mem    0pf

Please take a look when you are available, thanks!

Note: after preparing this patch I found the -std=gnu++11 in the check
performance script based on Jonathan's guidance. I can prepare a patch
for this tomorrow and get rid of the STD in the benchmark.

Yuao
0001-libstdc-optimize-ranges-for_each-for-segmented-itera.txt (text/plain, 4.6 KB)
From 03ad1aa9f65f1b76226d3ea8a81f30d415675dfe Mon Sep 17 00:00:00 2001
From: Yuao Ma <[email protected]>
Date: Thu, 20 Aug 2026 00:10:23 +0800
Subject: [PATCH] libstdc++: optimize ranges::for_each for segmented iterators

Similar to r17-3419-g2d11dfb0e2edd9, we can optimize std::for_each for
segemented iterators.

libstdc++-v3/ChangeLog:

	* include/bits/ranges_algo.h (ranges::__for_each): Add
	segemented iterators logic. Split out naive for-loop from ...
	(__for_each_fn::operator()): ... here.
	* testsuite/performance/25_algorithms/for_each.cc: Add
	benchmark for ranges::for_each.
---
 libstdc++-v3/include/bits/ranges_algo.h       | 33 +++++++++++++++++--
 .../performance/25_algorithms/for_each.cc     | 23 ++++++++++++-
 2 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index 4330d3e70b8..f4c70dc7029 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -211,6 +211,33 @@ namespace ranges
   template<typename _Iter, typename _Fp>
     using for_each_result = in_fun_result<_Iter, _Fp>;
 
+  // Apply __f to the result of applying __proj to each element in
+  // [__first, __last).
+  // Dispatches to __for_each_segment for segmented iterators
+  // (e.g. deque::iterator).
+  // Returns an iterator equal to __last.
+  template<typename _InputIterator, typename _Sentinel, typename _Function,
+	   typename _Proj>
+    constexpr _InputIterator
+    __for_each(_InputIterator __first, _Sentinel __last, _Function&& __f,
+	       _Proj& __proj)
+    {
+      if constexpr (__segmented_iterator<_InputIterator>
+		    && same_as<_InputIterator, _Sentinel>)
+	{
+	  std::__for_each_segment(__first, __last,
+	    [&](auto __lfirst, auto __llast)
+	    { return ranges::__for_each(__lfirst, __llast, __f, __proj); });
+	  return __last;
+	}
+      else
+	{
+	  for (; __first != __last; ++__first)
+	    std::__invoke(__f, std::__invoke(__proj, *__first));
+	  return __first;
+	}
+    }
+
   struct __for_each_fn
   {
     template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
@@ -219,9 +246,9 @@ namespace ranges
       constexpr for_each_result<_Iter, _Fun>
       operator()(_Iter __first, _Sent __last, _Fun __f, _Proj __proj = {}) const
       {
-	for (; __first != __last; ++__first)
-	  std::__invoke(__f, std::__invoke(__proj, *__first));
-	return { std::move(__first), std::move(__f) };
+	auto __end = ranges::__for_each(std::move(__first), std::move(__last), __f,
+					__proj);
+	return { std::move(__end), std::move(__f) };
       }
 
     template<input_range _Range, typename _Proj = identity,
diff --git a/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc b/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
index 37af5ab5981..dd8b5ae83b5 100644
--- a/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
+++ b/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
@@ -1,3 +1,4 @@
+// STD=gnu++20
 #include <testsuite_performance.h>
 
 #include <algorithm>
@@ -15,7 +16,20 @@ void bench_seq(const char* label, __gnu_test::time_counter& time,
   start_counters(time, resource);
   for (int i = 0; i < 20000; ++i)
     std::for_each(c.begin(), c.end(),
-                  [](T& x) { x = std::min<T>(100, std::max<T>(x, 10)); });
+                  [](T& x) { x = std::clamp<T>(x, 10, 100); });
+  stop_counters(time, resource);
+  report_performance(__FILE__, label, time, resource);
+  clear_counters(time, resource);
+}
+
+template <typename Container>
+void bench_seq_ranges(const char* label, __gnu_test::time_counter& time,
+                      __gnu_test::resource_counter& resource) {
+  using T = typename Container::value_type;
+  Container c(size, 1);
+  start_counters(time, resource);
+  for (int i = 0; i < 20000; ++i)
+    std::ranges::for_each(c, [](T& x) { x = std::clamp<T>(x, 10, 100); });
   stop_counters(time, resource);
   report_performance(__FILE__, label, time, resource);
   clear_counters(time, resource);
@@ -29,4 +43,11 @@ int main() {
   bench_seq<std::vector<int>>("std::for_each vector<int>", time, resource);
   bench_seq<std::deque<int>>("std::for_each deque<int>", time, resource);
   bench_seq<std::list<int>>("std::for_each list<int>", time, resource);
+
+  bench_seq_ranges<std::vector<int>>("std::ranges::for_each vector<int>", time,
+                                     resource);
+  bench_seq_ranges<std::deque<int>>("std::ranges::for_each deque<int>", time,
+                                    resource);
+  bench_seq_ranges<std::list<int>>("std::ranges::for_each list<int>", time,
+                                   resource);
 }
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.