Re: [PATCH] libstdc++: optimize std::for_each for segmented iterators

Yuao Ma <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel
Message-ID <CA+m0iR_EucqCk+irtRzJk6BVVo3rcbj1M=h3Mh1AGhH72KLmFA@mail.gmail.com>
On Wed, Aug 19, 2026 at 7:38 PM Jonathan Wakely <[email protected]> wrote:
>
> On Mon, 03 Aug 2026 at 23:11 +0800, Yuao Ma wrote:
> >On Mon, Aug 3, 2026 at 9:22 PM Patrick Palka <[email protected]> wrote:
> >>
> >> On Sun, 2 Aug 2026, Yuao Ma wrote:
> >>
> >> > Hi!
> >> >
> >> > Similar to ranges::distance, this patch optimizes std::for_each for
> >> > segmented iterators.
> >>
> >> Thanks for working on this.
> >>
> >> >
> >> > Fully tested on x86_64-linux with no regressions.
> >> >
> >> > Using the newly added benchmark, it shows a 3x improvement when using
> >> > std::for_each with std::deque.
> >> >
> >> > === Sun Aug  2 09:30:52 AM UTC 2026 ===
> >> > for_each.cc              std::for_each vector<int>    2r    1u    0s
> >> >       0mem    0pf
> >> > for_each.cc              std::for_each deque<int>    6r    6u    0s
> >> >      0mem    0pf
> >> > for_each.cc              std::for_each list<int>    13r   13u    0s
> >> >      0mem    0pf
> >> > === Sun Aug  2 09:31:08 AM 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   13u    0s
> >> >      0mem    0pf
> >>
> >> Interesting that even for_each benefits from this!  So this assumes
> >> it's significantly cheaper to iterate over the inner/local iterators
> >> than over the segmented iterators in general, makes sense.
> >>
> >> >
> >> > Please take a look when you are available, thanks!
> >> >
> >> > Yuao
> >> >
> >>
> >> > Subject: [PATCH] libstdc++: optimize std::for_each for segmented iterators
> >> >
> >> > Similar to r17-2859-g15505d012872dd, we can optimize std::for_each for
> >> > segemented iterators.
> >> >
> >> > libstdc++-v3/ChangeLog:
> >> >
> >> >       * include/bits/stl_algo.h (__for_each): Add segemented
> >> >       iterators logic. Split out naive for-loop from ...
> >> >       (for_each): ... here.
> >> >       * testsuite/performance/25_algorithms/for_each.cc: New test.
> >> > ---
> >> >  libstdc++-v3/include/bits/stl_algo.h          | 29 ++++++++++++++--
> >> >  .../performance/25_algorithms/for_each.cc     | 34 +++++++++++++++++++
> >> >  2 files changed, 61 insertions(+), 2 deletions(-)
> >> >  create mode 100644 libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
> >> >
> >> > diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
> >> > index 800c176cd5b..54d73ae8c62 100644
> >> > --- a/libstdc++-v3/include/bits/stl_algo.h
> >> > +++ b/libstdc++-v3/include/bits/stl_algo.h
> >> > @@ -132,6 +132,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >> >        return __first;
> >> >      }
> >> >
> >> > +  /// Apply __f 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 _Function>
> >> > +    _GLIBCXX20_CONSTEXPR
> >> > +    _InputIterator
> >> > +    __for_each(_InputIterator __first, _InputIterator __last, _Function& __f)
> >> > +    {
> >> > +#if __cplusplus >= 201703L
> >> > +      if constexpr (__enable_for_each_segment<_InputIterator>)
> >> > +     {
> >> > +       std::__for_each_segment(__first, __last,
> >> > +         [&](auto __lfirst, auto __llast)
> >> > +         { return std::__for_each(__lfirst, __llast, __f); });
> >> > +       return __last;
> >> > +     }
> >> > +      else
> >> > +#endif // C++17
> >>
> >> I think we could enable this optimization as far back as C++11 if we
> >> use a lambda template here (and #pragma GCC diagnostic ignored etc):
> >>
> >>   [&]<template _It>(_It __lfirst, _It __llast)
> >>
> >
> >Thanks for the suggestions. I use this approach in the new patch.
> >
> >> Besides that, looks good.
> >>
> >> (GCC and Clang accept lambda templates even in C++98 mode so in theory
> >> we could enable the optimization unconditionally if we resort to using
> >> C++98 compatible SFINAE, but I don't think it's worth it personally.)
> >>
> >> > +     {
> >> > +       for (; __first != __last; ++__first)
> >> > +         __f(*__first);
> >> > +       return __first;
> >> > +     }
> >> > +    }
> >> > +
> >> >    // set_difference
> >> >    // set_intersection
> >> >    // set_symmetric_difference
> >> > @@ -3813,8 +3839,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
> >> >        // concept requirements
> >> >        __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
> >> >        __glibcxx_requires_valid_range(__first, __last);
> >> > -      for (; __first != __last; ++__first)
> >> > -     __f(*__first);
> >> > +      std::__for_each(__first, __last, __f);
> >>
> >> I guess you're factoring this out to use elsewhere, e.g. ranges::for_each?
> >>
> >
> >Hmm, it is mostly for support the stateful functor passed in the
> >std::for_each, in which case we really need the parameter to be a
> >reference type. To use it for ranges::for_each at least it needs two
> >refactor: for function parameter it needs _Sentinel and _Proj, for the
> >function body it needs std::invoke. Not sure how to correctly includes
> >these given the current header structure, I think I will have a
> >standalone optimization for ranges::for_each, and let other algorithms
> >that could benefit from segmented iterators depend on either of them.
> >
> >> >        return __f; // N.B. [alg.foreach] says std::move(f) but it's redundant.
> >> >      }
> >> >
> >> > diff --git a/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc b/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
> >> > new file mode 100644
> >> > index 00000000000..22981eb9b64
> >> > --- /dev/null
> >> > +++ b/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
> >> > @@ -0,0 +1,34 @@
> >> > +// STD=gnu++17
> >> > +
> >> > +#include <testsuite_performance.h>
> >> > +
> >> > +#include <algorithm>
> >> > +#include <deque>
> >> > +#include <list>
> >> > +#include <vector>
> >> > +
> >> > +const std::size_t size = 8192;
> >> > +
> >> > +template <typename Container>
> >> > +void bench_seq(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::for_each(c.begin(), c.end(),
> >> > +                  [](T& x) { x = std::clamp<T>(x, 10, 100); });
> >> > +  stop_counters(time, resource);
> >> > +  report_performance(__FILE__, label, time, resource);
> >> > +  clear_counters(time, resource);
> >> > +}
> >> > +
> >> > +int main() {
> >> > +  using namespace __gnu_test;
> >> > +  time_counter time;
> >> > +  resource_counter resource;
> >> > +
> >> > +  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);
> >> > +}
> >> > --
> >> > 2.54.0
> >> >
> >>
>
> >From a8dc4e6bf0304ccf27b408a50ead08d3aff21d66 Mon Sep 17 00:00:00 2001
> >From: Yuao Ma <[email protected]>
> >Date: Mon, 3 Aug 2026 23:05:04 +0800
> >Subject: [PATCH] libstdc++: optimize std::for_each for segmented iterators
> >
> >Similar to r17-2859-g15505d012872dd, we can optimize std::for_each for
> >segemented iterators.
> >
> >libstdc++-v3/ChangeLog:
> >
> >       * include/bits/stl_algo.h (__for_each): Add segemented
> >       iterators logic. Split out naive for-loop from ...
> >       (for_each): ... here.
> >       * testsuite/performance/25_algorithms/for_each.cc: New test.
>
> OK for trunk with one change requested below ...
>
> >---
> > libstdc++-v3/include/bits/stl_algo.h          | 33 ++++++++++++++++--
> > .../performance/25_algorithms/for_each.cc     | 34 +++++++++++++++++++
> > 2 files changed, 65 insertions(+), 2 deletions(-)
> > create mode 100644 libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
> >
> >diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
> >index 800c176cd5b..3cd204dd871 100644
> >--- a/libstdc++-v3/include/bits/stl_algo.h
> >+++ b/libstdc++-v3/include/bits/stl_algo.h
> >@@ -132,6 +132,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       return __first;
> >     }
> >
> >+  /// Apply __f to each element in [__first, __last)
> >+  /// Dispatches to __for_each_segment for segmented iterators
> >+  /// (e.g. deque::iterator).
> >+  /// Returns an iterator equal to __last.
>
> Just "//" for these comments please, we don't need Doxygen to process
> this internal implementation details.
>

Fixed.

> >+#pragma GCC diagnostic push
> >+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> >+#pragma GCC diagnostic ignored "-Wc++20-extensions" // template lambda
> >+  template<typename _InputIterator, typename _Function>
> >+    _GLIBCXX20_CONSTEXPR
> >+    _InputIterator
> >+    __for_each(_InputIterator __first, _InputIterator __last, _Function& __f)
> >+    {
> >+#if __cplusplus >= 201103L
> >+      if constexpr (__enable_for_each_segment<_InputIterator>)
> >+      {
> >+        std::__for_each_segment(__first, __last,
> >+          [&]<typename _Iter>(_Iter __lfirst, _Iter __llast)
> >+          { return std::__for_each(__lfirst, __llast, __f); });
> >+        return __last;
> >+      }
> >+      else
> >+#endif // C++11
> >+      {
> >+        for (; __first != __last; ++__first)
> >+          __f(*__first);
> >+        return __first;
> >+      }
> >+    }
> >+#pragma GCC diagnostic pop
> >+
> >   // set_difference
> >   // set_intersection
> >   // set_symmetric_difference
> >@@ -3813,8 +3843,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
> >       // concept requirements
> >       __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
> >       __glibcxx_requires_valid_range(__first, __last);
> >-      for (; __first != __last; ++__first)
> >-      __f(*__first);
> >+      std::__for_each(__first, __last, __f);
> >       return __f; // N.B. [alg.foreach] says std::move(f) but it's redundant.
> >     }
> >
> >diff --git a/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc b/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
> >new file mode 100644
> >index 00000000000..22981eb9b64
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/performance/25_algorithms/for_each.cc
> >@@ -0,0 +1,34 @@
> >+// STD=gnu++17
>
> This is for std::clamp, right?
>
> We should probably remove the -std=gnu++11 in
> scripts/check_performance and just use the default -std option
> (currently gnu++20). I think the -std=gnu++11 was added when the
> default was still -std=gnu++98, so that we could test "new" C++11
> features. That can be a separate patch though.
>

I replaced the std::clamp with min/max, the STD is also removed.

Pushed as r17-3419-g2d11dfb0e2edd9, thanks for the review!

> >+
> >+#include <testsuite_performance.h>
> >+
> >+#include <algorithm>
> >+#include <deque>
> >+#include <list>
> >+#include <vector>
> >+
> >+const std::size_t size = 8192;
> >+
> >+template <typename Container>
> >+void bench_seq(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::for_each(c.begin(), c.end(),
> >+                  [](T& x) { x = std::clamp<T>(x, 10, 100); });
> >+  stop_counters(time, resource);
> >+  report_performance(__FILE__, label, time, resource);
> >+  clear_counters(time, resource);
> >+}
> >+
> >+int main() {
> >+  using namespace __gnu_test;
> >+  time_counter time;
> >+  resource_counter resource;
> >+
> >+  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);
> >+}
> >--
> >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.