[libcxx-commits] [libcxx] [libc++] Optimize {std, ranges}::distance for segmented iterators (PR #133612)
Peng Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Mar 30 10:42:15 PDT 2025
https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/133612
>From 695ac19cba9d49b3cb2f1e0cb76fcfe9488e5e75 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sat, 29 Mar 2025 22:29:33 -0400
Subject: [PATCH] Optimize {std,ranges}::distance for segmented iterators
---
libcxx/include/__iterator/distance.h | 52 +++++++----
.../benchmarks/iterators/distance.bench.cpp | 71 +++++++++++++++
.../iterator.operations/distance.pass.cpp | 89 ++++++++++++++-----
3 files changed, 176 insertions(+), 36 deletions(-)
create mode 100644 libcxx/test/benchmarks/iterators/distance.bench.cpp
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index 1732aa527f64a..8e63f64b6d86e 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -10,15 +10,20 @@
#ifndef _LIBCPP___ITERATOR_DISTANCE_H
#define _LIBCPP___ITERATOR_DISTANCE_H
+#include <__algorithm/for_each_segment.h>
+#include <__concepts/assignable.h>
#include <__config>
#include <__iterator/concepts.h>
#include <__iterator/incrementable_traits.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/segmented_iterator.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/size.h>
#include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
#include <__type_traits/remove_cvref.h>
+#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -26,25 +31,47 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _InputIter>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_InputIter>::difference_type
-__distance(_InputIter __first, _InputIter __last, input_iterator_tag) {
- typename iterator_traits<_InputIter>::difference_type __r(0);
+#if _LIBCPP_STD_VER >= 20
+template <class _Iter>
+using __iter_distance_t = std::iter_difference_t<_Iter>;
+#else
+template <class _Iter>
+using __iter_distance_t = typename iterator_traits<_Iter>::difference_type;
+#endif
+
+template <class _InputIter, class _Sent>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 __iter_distance_t<_InputIter>
+__distance(_InputIter __first, _Sent __last) {
+ __iter_distance_t<_InputIter> __r(0);
for (; __first != __last; ++__first)
++__r;
return __r;
}
-template <class _RandIter>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_RandIter>::difference_type
-__distance(_RandIter __first, _RandIter __last, random_access_iterator_tag) {
+template <class _RandIter, __enable_if_t<__has_random_access_iterator_category<_RandIter>::value, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 __iter_distance_t<_RandIter>
+__distance(_RandIter __first, _RandIter __last) {
return __last - __first;
}
+template <class _SegmentedIter,
+ __enable_if_t<!__has_random_access_iterator_category<_SegmentedIter>::value &&
+ __is_segmented_iterator<_SegmentedIter>::value,
+ int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 __iter_distance_t<_SegmentedIter>
+__distance(_SegmentedIter __first, _SegmentedIter __last) {
+ __iter_distance_t<_SegmentedIter> __r(0);
+ using __local_iterator = typename __segmented_iterator_traits<_SegmentedIter>::__local_iterator;
+ std::__for_each_segment(__first, __last, [&__r](__local_iterator __lfirst, __local_iterator __llast) {
+ __r += std::__distance(__lfirst, __llast);
+ });
+ return __r;
+}
+
template <class _InputIter>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_InputIter>::difference_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 __iter_distance_t<_InputIter>
distance(_InputIter __first, _InputIter __last) {
- return std::__distance(__first, __last, typename iterator_traits<_InputIter>::iterator_category());
+ return std::__distance(__first, __last);
}
#if _LIBCPP_STD_VER >= 20
@@ -56,12 +83,7 @@ struct __distance {
template <class _Ip, sentinel_for<_Ip> _Sp>
requires(!sized_sentinel_for<_Sp, _Ip>)
_LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip __first, _Sp __last) const {
- iter_difference_t<_Ip> __n = 0;
- while (__first != __last) {
- ++__first;
- ++__n;
- }
- return __n;
+ return std::__distance(std::move(__first), std::move(__last));
}
template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
diff --git a/libcxx/test/benchmarks/iterators/distance.bench.cpp b/libcxx/test/benchmarks/iterators/distance.bench.cpp
new file mode 100644
index 0000000000000..db9e6a9201658
--- /dev/null
+++ b/libcxx/test/benchmarks/iterators/distance.bench.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+ auto std_distance = [](auto first, auto last) { return std::distance(first, last); };
+
+ // {std,ranges}::distance
+ {
+ auto bm = []<class Container>(std::string name, auto distance, std::size_t seg_size) {
+ benchmark::RegisterBenchmark(
+ name,
+ [distance, seg_size](auto& st) {
+ std::size_t const size = st.range(0);
+ std::size_t const segments = (size + seg_size - 1) / seg_size;
+ Container c(segments);
+ for (std::size_t i = 0, n = size; i < segments; ++i, n -= seg_size) {
+ c[i].resize(std::min(seg_size, n));
+ }
+
+ auto view = c | std::views::join;
+ auto first = view.begin();
+ auto last = view.end();
+
+ for ([[maybe_unused]] auto _ : st) {
+ benchmark::DoNotOptimize(c);
+ auto result = distance(first, last);
+ benchmark::DoNotOptimize(result);
+ }
+ })
+ ->Arg(50) // non power-of-two
+ ->Arg(1024)
+ ->Arg(4096)
+ ->Arg(8192)
+ ->Arg(1 << 14)
+ ->Arg(1 << 16)
+ ->Arg(1 << 18)
+ ->Arg(1 << 20);
+ };
+ bm.operator()<std::vector<std::vector<int>>>("std::distance(join_view(vector<vector<int>>))", std_distance, 256);
+ bm.operator()<std::deque<std::deque<int>>>("std::distance(join_view(deque<deque<int>>))", std_distance, 256);
+ bm.operator()<std::vector<std::vector<int>>>(
+ "rng::distance(join_view(vector<vector<int>>)", std::ranges::distance, 256);
+ bm.operator()<std::deque<std::deque<int>>>(
+ "rng::distance(join_view(deque<deque<int>>)", std::ranges::distance, 256);
+
+ // bm.operator()<std::vector<std::vector<int>>>("std::distance(join_view(vector<vector<int>>))", std_distance, 1024);
+ // bm.operator()<std::deque<std::deque<int>>>("std::distance(join_view(deque<deque<int>>))", std_distance, 1024);
+ // bm.operator()<std::vector<std::vector<int>>>("rng::distance(join_view(vector<vector<int>>)", std::ranges::distance, 1024);
+ // bm.operator()<std::deque<std::deque<int>>>("rng::distance(join_view(deque<deque<int>>)", std::ranges::distance, 1024);
+ }
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/std/iterators/iterator.primitives/iterator.operations/distance.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/iterator.operations/distance.pass.cpp
index 13caefff92365..917abc4afee14 100644
--- a/libcxx/test/std/iterators/iterator.primitives/iterator.operations/distance.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/iterator.operations/distance.pass.cpp
@@ -16,38 +16,85 @@
// Iter::difference_type
// distance(Iter first, Iter last); // constexpr in C++17
-#include <iterator>
+#include <array>
#include <cassert>
+#include <deque>
+#include <iterator>
+#include <vector>
#include <type_traits>
#include "test_macros.h"
#include "test_iterators.h"
template <class It>
-TEST_CONSTEXPR_CXX17
-void check_distance(It first, It last, typename std::iterator_traits<It>::difference_type dist)
-{
- typedef typename std::iterator_traits<It>::difference_type Difference;
- static_assert(std::is_same<decltype(std::distance(first, last)), Difference>::value, "");
- assert(std::distance(first, last) == dist);
+TEST_CONSTEXPR_CXX17 void check_distance(It first, It last, typename std::iterator_traits<It>::difference_type dist) {
+ typedef typename std::iterator_traits<It>::difference_type Difference;
+ static_assert(std::is_same<decltype(std::distance(first, last)), Difference>::value, "");
+ assert(std::distance(first, last) == dist);
}
-TEST_CONSTEXPR_CXX17 bool tests()
-{
- const char* s = "1234567890";
- check_distance(cpp17_input_iterator<const char*>(s), cpp17_input_iterator<const char*>(s+10), 10);
- check_distance(forward_iterator<const char*>(s), forward_iterator<const char*>(s+10), 10);
- check_distance(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s+10), 10);
- check_distance(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+10), 10);
- check_distance(s, s+10, 10);
- return true;
+#if TEST_STD_VER >= 20
+template <class It>
+TEST_CONSTEXPR_CXX20 void check_ranges_distance(It first, It last, std::iter_difference_t<It> dist) {
+ using Difference = std::iter_difference_t<It>;
+ static_assert(std::is_same<decltype(std::ranges::distance(first, last)), Difference>::value, "");
+ assert(std::ranges::distance(first, last) == dist);
+}
+#endif
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+ const char* s = "1234567890";
+ check_distance(cpp17_input_iterator<const char*>(s), cpp17_input_iterator<const char*>(s + 10), 10);
+ check_distance(forward_iterator<const char*>(s), forward_iterator<const char*>(s + 10), 10);
+ check_distance(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s + 10), 10);
+ check_distance(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s + 10), 10);
+ check_distance(s, s + 10, 10);
+
+#if TEST_STD_VER >= 20
+ check_ranges_distance(forward_iterator(s), forward_iterator(s + 10), 10);
+ check_ranges_distance(bidirectional_iterator(s), bidirectional_iterator(s + 10), 10);
+ check_ranges_distance(random_access_iterator(s), random_access_iterator(s + 10), 10);
+ check_ranges_distance(s, s + 10, 10);
+
+ {
+ using Container = std::vector<std::vector<int>>;
+ Container c;
+ auto view = c | std::views::join;
+ Container::difference_type n = 0;
+ for (std::size_t i = 0; i < 10; ++i) {
+ n += i;
+ c.push_back(Container::value_type(i));
+ }
+ assert(std::distance(view.begin(), view.end()) == n);
+ assert(std::ranges::distance(view.begin(), view.end()) == n);
+ }
+ {
+ using Container = std::array<std::array<char, 3>, 10>;
+ Container c;
+ auto view = c | std::views::join;
+ assert(std::distance(view.begin(), view.end()) == 30);
+ assert(std::ranges::distance(view.begin(), view.end()) == 30);
+ }
+ if (!TEST_IS_CONSTANT_EVALUATED) {
+ using Container = std::deque<std::deque<double>>;
+ Container c;
+ auto view = c | std::views::join;
+ Container::difference_type n = 0;
+ for (std::size_t i = 0; i < 10; ++i) {
+ n += i;
+ c.push_back(Container::value_type(i));
+ }
+ assert(std::distance(view.begin(), view.end()) == n);
+ assert(std::ranges::distance(view.begin(), view.end()) == n);
+ }
+#endif
+ return true;
}
-int main(int, char**)
-{
- tests();
+int main(int, char**) {
+ tests();
#if TEST_STD_VER >= 17
- static_assert(tests(), "");
+ static_assert(tests(), "");
#endif
- return 0;
+ return 0;
}
More information about the libcxx-commits
mailing list