[PATCH] libstdc++: optimize ranges::for_each for segmented iterators
Yuao Ma
addr2line@gmail.com
Wed Aug 19 16:22:15 GMT 2026
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
-------------- next part --------------
From 03ad1aa9f65f1b76226d3ea8a81f30d415675dfe Mon Sep 17 00:00:00 2001
From: Yuao Ma <c8ef@outlook.com>
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
More information about the Libstdc++
mailing list