[libcxx-commits] [libcxx] [libc++] Optimize ranges::copy for forward_iterator and segmented_iterator (PR #120134)
Peng Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Mar 23 08:34:17 PDT 2025
https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/120134
>From 2863a813afee1f5be1564a99d043bc43fe307575 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Mon, 16 Dec 2024 13:30:56 -0500
Subject: [PATCH 1/3] Speed-up range operations in vector<bool>
---
libcxx/include/__algorithm/copy.h | 79 +++++++++++++
libcxx/include/__bit_reference | 11 ++
libcxx/include/__vector/vector_bool.h | 1 -
.../vector_bool_operations.bench.cpp | 110 ++++++++++++++++++
.../alg.copy/copy.pass.cpp | 33 ++++++
.../test/std/containers/from_range_helpers.h | 38 ++++++
6 files changed, 271 insertions(+), 1 deletion(-)
create mode 100644 libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index ea98031df11ad..955389ad8f166 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -14,11 +14,13 @@
#include <__algorithm/min.h>
#include <__config>
#include <__fwd/bit_reference.h>
+#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/segmented_iterator.h>
#include <__memory/pointer_traits.h>
#include <__type_traits/common_type.h>
#include <__type_traits/enable_if.h>
+#include <__type_traits/is_convertible.h>
#include <__utility/move.h>
#include <__utility/pair.h>
@@ -154,6 +156,25 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
return __result;
}
+template <class _InIter,
+ class _Sent,
+ __enable_if_t<__has_input_iterator_category<_InIter>::value &&
+ !__has_random_access_iterator_category<_InIter>::value,
+ int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_InIter>::difference_type
+__iter_sent_distance(_InIter __first, _Sent __last) {
+ typename iterator_traits<_InIter>::difference_type __r(0);
+ for (; __first != __last; ++__first)
+ ++__r;
+ return __r;
+}
+
+template <class _InIter, class _Sent, __enable_if_t<__has_random_access_iterator_category<_InIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_InIter>::difference_type
+__iter_sent_distance(_InIter __first, _Sent __last) {
+ return static_cast<typename iterator_traits<_InIter>::difference_type>(__last - __first);
+}
+
struct __copy_impl {
template <class _InIter, class _Sent, class _OutIter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
@@ -227,6 +248,64 @@ struct __copy_impl {
return std::make_pair(__last, std::__copy_unaligned(__first, __last, __result));
}
+ template <class _InIter,
+ class _Sent,
+ class _Cp,
+ __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
+ __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+ is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
+ int> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
+ operator()(_InIter __first, _Sent __last, __bit_iterator<_Cp, false> __result) const {
+ using _It = __bit_iterator<_Cp, false>;
+ using __storage_type = typename _It::__storage_type;
+#if _LIBCPP_STD_VER >= 20
+ __storage_type __n = static_cast<__storage_type>(std::ranges::distance(__first, __last));
+#else
+ __storage_type __n = static_cast<__storage_type>(std::__iter_sent_distance(__first, __last));
+#endif
+ const unsigned __bits_per_word = _It::__bits_per_word;
+
+ if (__first != __last) {
+ // do first partial word, if present
+ if (__result.__ctz_ != 0) {
+ __storage_type __clz = static_cast<__storage_type>(__bits_per_word - __result.__ctz_);
+ __storage_type __dn = std::min(__clz, __n);
+ __storage_type __w = *__result.__seg_;
+ __storage_type __m = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz - __dn));
+ __w &= ~__m;
+ for (__storage_type __i = 0; __i < __dn; ++__i, ++__first)
+ __w |= static_cast<__storage_type>(*__first) << __result.__ctz_++;
+ *__result.__seg_ = __w;
+ if (__result.__ctz_ == __bits_per_word) {
+ __result.__ctz_ = 0;
+ ++__result.__seg_;
+ }
+ __n -= __dn;
+ }
+ }
+ // do middle whole words, if present
+ __storage_type __nw = __n / __bits_per_word;
+ __n -= __nw * __bits_per_word;
+ for (; __nw; --__nw) {
+ __storage_type __w = 0;
+ for (__storage_type __i = 0; __i < __bits_per_word; ++__i, ++__first)
+ __w |= static_cast<__storage_type>(*__first) << __i;
+ *__result.__seg_++ = __w;
+ }
+ // do last partial word, if present
+ if (__n) {
+ __storage_type __w = 0;
+ for (__storage_type __i = 0; __i < __n; ++__i, ++__first)
+ __w |= static_cast<__storage_type>(*__first) << __i;
+ __storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
+ *__result.__seg_ &= ~__m;
+ *__result.__seg_ |= __w;
+ __result.__ctz_ = __n;
+ }
+ return std::make_pair(std::move(__first), std::move(__result));
+ }
+
// At this point, the iterators have been unwrapped so any `contiguous_iterator` has been unwrapped to a pointer.
template <class _In, class _Out, __enable_if_t<__can_lower_copy_assignment_to_memmove<_In, _Out>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_In*, _Out*>
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index f5c22fc0a3ade..3d8c333a7614f 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -35,6 +35,7 @@
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_unsigned.h>
+#include <__type_traits/is_convertible.h>
#include <__type_traits/void_t.h>
#include <__utility/pair.h>
#include <__utility/swap.h>
@@ -463,6 +464,16 @@ private:
template <class _Dp>
friend struct __bit_array;
+ template <class _InIter,
+ class _Sent,
+ class _Dp,
+ __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
+ __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+ is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
+ int> >
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 friend pair<_InIter, __bit_iterator<_Dp, false> >
+ __copy_impl::operator()(_InIter __first, _Sent __last, __bit_iterator<_Dp, false> __result) const;
+
template <bool _FillVal, class _Dp>
_LIBCPP_CONSTEXPR_SINCE_CXX20 friend void
__fill_n_bool(__bit_iterator<_Dp, false> __first, typename __size_difference_type_traits<_Dp>::size_type __n);
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index 569cc5ea898bc..32d818038dfe2 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -174,7 +174,6 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
auto __n = static_cast<size_type>(ranges::distance(__range));
__init_with_size(ranges::begin(__range), ranges::end(__range), __n);
-
} else {
__init_with_sentinel(ranges::begin(__range), ranges::end(__range));
}
diff --git a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
new file mode 100644
index 0000000000000..a434bdbbc6a3e
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "ContainerBenchmarks.h"
+#include "../../std/containers/from_range_helpers.h"
+#include "../GenerateInput.h"
+#include "test_iterators.h"
+
+using namespace ContainerBenchmarks;
+
+using vb_iter = std::vector<bool>::iterator;
+
+// Benchmarks for forward_iterator or forward_range
+
+BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
+ forward_iterator,
+ getRandomIntegerInputs<bool>,
+ forward_iterator<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
+ forward_range,
+ getRandomIntegerInputs<bool>,
+ forward_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+ BM_AssignIterIter<std::vector<bool>>, forward_iterator, getRandomIntegerInputs<bool>, forward_iterator<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+ BM_AssignRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
+ forward_iterator,
+ getRandomIntegerInputs<bool>,
+ forward_iterator<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+ BM_InsertRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+ BM_AppendRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+// Benchmarks for random_access_iterator or random_access_range
+
+BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
+ random_access_iterator,
+ getRandomIntegerInputs<bool>,
+ random_access_iterator<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
+ random_access_range,
+ getRandomIntegerInputs<bool>,
+ random_access_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_AssignIterIter<std::vector<bool>>,
+ random_access_iterator,
+ getRandomIntegerInputs<bool>,
+ random_access_iterator<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_AssignRange<std::vector<bool>>,
+ random_access_range,
+ getRandomIntegerInputs<bool>,
+ random_access_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
+ random_access_iterator,
+ getRandomIntegerInputs<bool>,
+ random_access_iterator<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_InsertRange<std::vector<bool>>,
+ random_access_range,
+ getRandomIntegerInputs<bool>,
+ random_access_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_AppendRange<std::vector<bool>>,
+ random_access_range,
+ getRandomIntegerInputs<bool>,
+ random_access_range_wrapper<vb_iter>())
+ ->Arg(5140480);
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
index cfcaf1c8a6dd7..21f1e3b062365 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
@@ -15,6 +15,7 @@
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
#include <algorithm>
+#include <array>
#include <cassert>
#include <vector>
@@ -64,6 +65,30 @@ struct TestInIters {
}
};
+template <std::size_t N>
+struct TestFwdIterInBitIterOut {
+ std::array<bool, N> in = {};
+ template <class FwdIter>
+ TEST_CONSTEXPR_CXX20 void operator()() {
+ for (std::size_t i = 0; i < in.size(); i += 2)
+ in[i] = true;
+
+ { // Test with full bytes
+ std::vector<bool> out(N);
+ std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin());
+ for (std::size_t i = 0; i < N; ++i)
+ assert(out[i] == static_cast<bool>(in[i]));
+ }
+ { // Test with partial bytes in both front and back
+ std::vector<bool> out(N + 8);
+ std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin() + 4);
+ for (std::size_t i = 0; i < N; ++i)
+ assert(out[i + 4] == static_cast<bool>(in[i]));
+ }
+ }
+};
+
+
TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
std::vector<bool> in(N, false);
for (std::size_t i = 0; i < N; i += 2)
@@ -259,6 +284,14 @@ TEST_CONSTEXPR_CXX20 bool test() {
}
}
+ { // Test std::copy() with forward_iterator-pair inputs and vector<bool>::iterator output
+ types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<8>());
+ types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<19>());
+ types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<32>());
+ types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<64>());
+ types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<299>());
+ }
+
return true;
}
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index edf1d6ce528d7..3b703dbe4d9b7 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -50,6 +50,44 @@ constexpr auto wrap_input(std::vector<T>& input) {
return std::ranges::subrange(std::move(b), std::move(e));
}
+template <class Iter, class Sent = Iter>
+class forward_range_wrapper {
+public:
+ using _Iter = forward_iterator<Iter>;
+ using _Sent = sentinel_wrapper<_Iter>;
+
+ forward_range_wrapper() = default;
+ forward_range_wrapper(Iter begin, Iter end) : begin_(std::move(begin)), end_(std::move(end)) {}
+ _Iter begin() { return _Iter(std::move(begin_)); }
+ _Sent end() { return _Sent(_Iter(std::move(end_))); }
+
+private:
+ Iter begin_;
+ Sent end_;
+};
+
+template <class Iter, class Sent>
+forward_range_wrapper(Iter, Sent) -> forward_range_wrapper<Iter, Sent>;
+
+template <class Iter, class Sent = Iter>
+class random_access_range_wrapper {
+public:
+ using _Iter = cpp20_random_access_iterator<Iter>;
+ using _Sent = sized_sentinel<_Iter>;
+
+ random_access_range_wrapper() = default;
+ random_access_range_wrapper(Iter begin, Iter end) : begin_(std::move(begin)), end_(std::move(end)) {}
+ _Iter begin() { return _Iter(std::move(begin_)); }
+ _Sent end() { return _Sent(_Iter(std::move(end_))); }
+
+private:
+ Iter begin_;
+ Sent end_;
+};
+
+template <class Iter, class Sent>
+random_access_range_wrapper(Iter, Sent) -> random_access_range_wrapper<Iter, Sent>;
+
struct KeyValue {
int key; // Only the key is considered for equality comparison.
char value; // Allows distinguishing equivalent instances.
>From 1924d9eecbcdd23cf3f2f1ad4a9d79a430f50bd8 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sat, 22 Mar 2025 10:53:54 -0400
Subject: [PATCH 2/3] Fix mask and add copy benchmark
---
libcxx/include/__algorithm/copy.h | 24 +++++++---
libcxx/include/__bit_reference | 7 +--
.../algorithms/modifying/copy.bench.cpp | 45 +++++++++++++++++--
.../alg.copy/copy.pass.cpp | 12 ++---
4 files changed, 69 insertions(+), 19 deletions(-)
diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index 955389ad8f166..2146f0952e1fe 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -242,21 +242,33 @@ struct __copy_impl {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<__bit_iterator<_Cp, _IsConst>, __bit_iterator<_Cp, false> >
operator()(__bit_iterator<_Cp, _IsConst> __first,
__bit_iterator<_Cp, _IsConst> __last,
- __bit_iterator<_Cp, false> __result) const {
+ __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
if (__first.__ctz_ == __result.__ctz_)
return std::make_pair(__last, std::__copy_aligned(__first, __last, __result));
return std::make_pair(__last, std::__copy_unaligned(__first, __last, __result));
}
+ template <class _InIter,
+ class _OutIter,
+ class _Cp,
+ bool _IsConst,
+ __enable_if_t<__is_segmented_iterator<_InIter>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
+ operator()(_InIter __first, _InIter __last, __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
+ std::__for_each_segment(__first, __last, _CopySegment<_InIter, __bit_iterator<_Cp, false> >(__result));
+ return std::make_pair(__last, std::move(__result));
+ }
+
template <class _InIter,
class _Sent,
class _Cp,
- __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
- __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+ __enable_if_t<!__is_segmented_iterator<_InIter>::value &&
+ (__has_forward_iterator_category<_InIter>::value ||
+ __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
- operator()(_InIter __first, _Sent __last, __bit_iterator<_Cp, false> __result) const {
+ operator()(_InIter __first, _Sent __last, __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
using _It = __bit_iterator<_Cp, false>;
using __storage_type = typename _It::__storage_type;
#if _LIBCPP_STD_VER >= 20
@@ -272,7 +284,7 @@ struct __copy_impl {
__storage_type __clz = static_cast<__storage_type>(__bits_per_word - __result.__ctz_);
__storage_type __dn = std::min(__clz, __n);
__storage_type __w = *__result.__seg_;
- __storage_type __m = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz - __dn));
+ __storage_type __m = std::__middle_mask<__storage_type>(__clz - __dn, __result.__ctz_);
__w &= ~__m;
for (__storage_type __i = 0; __i < __dn; ++__i, ++__first)
__w |= static_cast<__storage_type>(*__first) << __result.__ctz_++;
@@ -298,7 +310,7 @@ struct __copy_impl {
__storage_type __w = 0;
for (__storage_type __i = 0; __i < __n; ++__i, ++__first)
__w |= static_cast<__storage_type>(*__first) << __i;
- __storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
+ __storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
*__result.__seg_ &= ~__m;
*__result.__seg_ |= __w;
__result.__ctz_ = __n;
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 3d8c333a7614f..19fae037803e5 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -33,9 +33,9 @@
#include <__type_traits/desugars_to.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_convertible.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_unsigned.h>
-#include <__type_traits/is_convertible.h>
#include <__type_traits/void_t.h>
#include <__utility/pair.h>
#include <__utility/swap.h>
@@ -467,8 +467,9 @@ private:
template <class _InIter,
class _Sent,
class _Dp,
- __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
- __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+ __enable_if_t<!__is_segmented_iterator<_InIter>::value &&
+ (__has_forward_iterator_category<_InIter>::value ||
+ __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
int> >
_LIBCPP_CONSTEXPR_SINCE_CXX14 friend pair<_InIter, __bit_iterator<_Dp, false> >
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
index 3549d918478bd..c6ace90965789 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
@@ -18,6 +18,7 @@
#include "benchmark/benchmark.h"
#include "../../GenerateInput.h"
+#include "test_iterators.h"
#include "test_macros.h"
int main(int argc, char** argv) {
@@ -25,14 +26,15 @@ int main(int argc, char** argv) {
// {std,ranges}::copy(normal container)
{
- auto bm = []<class Container>(std::string name, auto copy) {
+ auto bm = []<class ContainerIn, class ContainerOut = std::vector<typename ContainerIn::value_type>>(
+ std::string name, auto copy) {
benchmark::RegisterBenchmark(name, [copy](auto& st) {
std::size_t const n = st.range(0);
- using ValueType = typename Container::value_type;
- Container c;
+ using ValueType = typename ContainerIn::value_type;
+ ContainerIn c;
std::generate_n(std::back_inserter(c), n, [] { return Generate<ValueType>::random(); });
- std::vector<ValueType> out(n);
+ ContainerOut out(n);
for ([[maybe_unused]] auto _ : st) {
benchmark::DoNotOptimize(c);
@@ -42,12 +44,21 @@ int main(int argc, char** argv) {
}
})->Range(8, 1 << 20);
};
+ // Copy from normal containers to vector<int>
bm.operator()<std::vector<int>>("std::copy(vector<int>)", std_copy);
bm.operator()<std::deque<int>>("std::copy(deque<int>)", std_copy);
bm.operator()<std::list<int>>("std::copy(list<int>)", std_copy);
bm.operator()<std::vector<int>>("rng::copy(vector<int>)", std::ranges::copy);
bm.operator()<std::deque<int>>("rng::copy(deque<int>)", std::ranges::copy);
bm.operator()<std::list<int>>("rng::copy(list<int>)", std::ranges::copy);
+
+ // Copy from normal containers to vector<bool>
+ bm.operator()<std::vector<int>, std::vector<bool>>("std::copy(vector<int>, std::vector<bool>)", std_copy);
+ bm.operator()<std::deque<int>, std::vector<bool>>("std::copy(deque<int>, std::vector<bool>)", std_copy);
+ bm.operator()<std::list<int>, std::vector<bool>>("std::copy(list<int>, std::vector<bool>)", std_copy);
+ bm.operator()<std::vector<int>, std::vector<bool>>("rng::copy(vector<int>, std::vector<bool>)", std::ranges::copy);
+ bm.operator()<std::deque<int>, std::vector<bool>>("rng::copy(deque<int>, std::vector<bool>)", std::ranges::copy);
+ bm.operator()<std::list<int>, std::vector<bool>>("rng::copy(list<int>, std::vector<bool>)", std::ranges::copy);
}
// {std,ranges}::copy(vector<bool>)
@@ -76,6 +87,32 @@ int main(int argc, char** argv) {
#endif
}
+ // {std,ranges}::copy(forward_iterator, forward_iterator, vector<bool>)
+ {
+ auto bm = []<template <class> class Iter>(std::string name, auto copy) {
+ benchmark::RegisterBenchmark(name, [copy](auto& st) {
+ std::size_t const n = st.range(0);
+ std::vector<int> in(n, 1);
+ std::vector<bool> out(n);
+ auto first = Iter(in.begin());
+ auto last = Iter(in.end());
+ auto dst = out.begin();
+ for ([[maybe_unused]] auto _ : st) {
+ benchmark::DoNotOptimize(in);
+ benchmark::DoNotOptimize(out);
+ auto result = copy(first, last, dst);
+ benchmark::DoNotOptimize(result);
+ }
+ })->Range(64, 1 << 20);
+ };
+ bm.operator()<forward_iterator>("std::copy(forward_iterator, vector<bool>)", std_copy);
+ bm.operator()<random_access_iterator>("std::copy(random_access_iterator, vector<bool>)", std_copy);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
+ bm.operator()<forward_iterator>("rng::copy(forward_iterator, vector<bool>)", std::ranges::copy);
+ bm.operator()<random_access_iterator>("rng::copy(random_access_iterator, vector<bool>)", std::ranges::copy);
+#endif
+ }
+
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
index 21f1e3b062365..10909b36b0b4a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
@@ -66,7 +66,7 @@ struct TestInIters {
};
template <std::size_t N>
-struct TestFwdIterInBitIterOut {
+struct CopyFromForwardIterToBitIter {
std::array<bool, N> in = {};
template <class FwdIter>
TEST_CONSTEXPR_CXX20 void operator()() {
@@ -285,11 +285,11 @@ TEST_CONSTEXPR_CXX20 bool test() {
}
{ // Test std::copy() with forward_iterator-pair inputs and vector<bool>::iterator output
- types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<8>());
- types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<19>());
- types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<32>());
- types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<64>());
- types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<299>());
+ types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<8>());
+ types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<19>());
+ types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<32>());
+ types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<64>());
+ types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<299>());
}
return true;
>From d6119cd5e28ad3db049797c7b91812f995daba24 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sun, 23 Mar 2025 10:52:23 -0400
Subject: [PATCH 3/3] Add vector<bool> benchmark and cover more segmented
iterators
---
libcxx/include/__algorithm/copy.h | 6 +-
libcxx/include/__bit_reference | 1 +
.../algorithms/modifying/copy.bench.cpp | 2 +
.../sequence/sequence_container_benchmarks.h | 191 +++++++++++++++++-
.../containers/sequence/vector.bench.cpp | 1 +
.../vector_bool_operations.bench.cpp | 110 ----------
.../alg.copy/copy.pass.cpp | 128 +++++++++---
.../test/std/containers/from_range_helpers.h | 4 +-
8 files changed, 293 insertions(+), 150 deletions(-)
delete mode 100644 libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index 2146f0952e1fe..bee3b5d9ff0ea 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -248,11 +248,7 @@ struct __copy_impl {
return std::make_pair(__last, std::__copy_unaligned(__first, __last, __result));
}
- template <class _InIter,
- class _OutIter,
- class _Cp,
- bool _IsConst,
- __enable_if_t<__is_segmented_iterator<_InIter>::value, int> = 0>
+ template <class _InIter, class _Cp, __enable_if_t<__is_segmented_iterator<_InIter>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
operator()(_InIter __first, _InIter __last, __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
std::__for_each_segment(__first, __last, _CopySegment<_InIter, __bit_iterator<_Cp, false> >(__result));
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 19fae037803e5..ba4b53082ccf7 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -27,6 +27,7 @@
#include <__functional/identity.h>
#include <__fwd/bit_reference.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/segmented_iterator.h>
#include <__memory/construct_at.h>
#include <__memory/pointer_traits.h>
#include <__type_traits/conditional.h>
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
index c6ace90965789..6a175465a1147 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
@@ -52,6 +52,7 @@ int main(int argc, char** argv) {
bm.operator()<std::deque<int>>("rng::copy(deque<int>)", std::ranges::copy);
bm.operator()<std::list<int>>("rng::copy(list<int>)", std::ranges::copy);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
// Copy from normal containers to vector<bool>
bm.operator()<std::vector<int>, std::vector<bool>>("std::copy(vector<int>, std::vector<bool>)", std_copy);
bm.operator()<std::deque<int>, std::vector<bool>>("std::copy(deque<int>, std::vector<bool>)", std_copy);
@@ -59,6 +60,7 @@ int main(int argc, char** argv) {
bm.operator()<std::vector<int>, std::vector<bool>>("rng::copy(vector<int>, std::vector<bool>)", std::ranges::copy);
bm.operator()<std::deque<int>, std::vector<bool>>("rng::copy(deque<int>, std::vector<bool>)", std::ranges::copy);
bm.operator()<std::list<int>, std::vector<bool>>("rng::copy(list<int>, std::vector<bool>)", std::ranges::copy);
+#endif
}
// {std,ranges}::copy(vector<bool>)
diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index dcd251d6997dd..db9c45028010f 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -22,6 +22,7 @@
#include "benchmark/benchmark.h"
#include "test_iterators.h"
#include "../../GenerateInput.h"
+#include "../../../std/containers/from_range_helpers.h"
namespace support {
@@ -172,8 +173,8 @@ void sequence_container_benchmarks(std::string container) {
bool toggle = false;
for ([[maybe_unused]] auto _ : st) {
std::vector<ValueType>& in = toggle ? in1 : in2;
- auto first = in.data();
- auto last = in.data() + in.size();
+ auto first = in.begin();
+ auto last = in.end();
c.assign(cpp17_input_iterator(first), cpp17_input_iterator(last));
toggle = !toggle;
DoNotOptimizeData(c);
@@ -237,8 +238,8 @@ void sequence_container_benchmarks(std::string container) {
std::vector<ValueType> in;
std::generate_n(std::back_inserter(in), size, gen);
DoNotOptimizeData(in);
- auto first = in.data();
- auto last = in.data() + in.size();
+ auto first = in.begin();
+ auto last = in.end();
const int small = 100; // arbitrary
Container c;
@@ -264,8 +265,8 @@ void sequence_container_benchmarks(std::string container) {
std::vector<ValueType> in;
std::generate_n(std::back_inserter(in), size, gen);
DoNotOptimizeData(in);
- auto first = in.data();
- auto last = in.data() + in.size();
+ auto first = in.begin();
+ auto last = in.end();
const int overflow = size / 10; // 10% of elements won't fit in the vector when we insert
Container c;
@@ -290,8 +291,8 @@ void sequence_container_benchmarks(std::string container) {
std::vector<ValueType> in;
std::generate_n(std::back_inserter(in), size, gen);
DoNotOptimizeData(in);
- auto first = in.data();
- auto last = in.data() + in.size();
+ auto first = in.begin();
+ auto last = in.end();
auto const overflow = 9 * (size / 10); // 90% of elements won't fit in the vector when we insert
Container c;
@@ -448,6 +449,180 @@ void sequence_container_benchmarks(std::string container) {
}
});
}
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Additional benchmarks for vector<bool> iterator-pair and range-based operations
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ static constexpr bool is_vector_bool = requires {
+ typename Container::allocator_type;
+ } && std::same_as<std::remove_cvref_t<Container>, std::vector<bool, typename Container::allocator_type>>;
+
+ if constexpr (is_vector_bool) {
+ auto bench_vb = [&](std::string operation, auto f) {
+ benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(1024)->Arg(1 << 16)->Arg(1 << 20);
+ };
+
+ { // iterator-pair ctor
+ auto bm = [&generators, &bench_vb, &tostr]<template <class> class Iter>(std::string iter) {
+ for (auto gen : generators)
+ bench_vb("ctor(" + iter + ", " + iter + ")" + tostr(gen), [gen](auto& st) {
+ auto const size = st.range(0);
+ std::vector<int> in;
+ std::generate_n(std::back_inserter(in), size, gen);
+ benchmark::DoNotOptimize(in);
+ const auto begin = Iter(in.begin());
+ const auto end = Iter(in.end());
+ benchmark::DoNotOptimize(in);
+
+ for ([[maybe_unused]] auto _ : st) {
+ Container c(begin, end); // we assume the destructor doesn't dominate the benchmark
+ DoNotOptimizeData(c);
+ }
+ });
+ };
+ bm.template operator()<forward_iterator>("fwd_iter");
+ bm.template operator()<random_access_iterator>("ra_iter");
+ }
+ { // iterator-pair assignment
+ auto bm = [&generators, &bench_vb, &tostr]<template <class> class Iter>(std::string iter) {
+ for (auto gen : generators)
+ bench_vb("assign(" + iter + ", " + iter + ")" + tostr(gen), [gen](auto& st) {
+ auto const size = st.range(0);
+ std::vector<int> in1, in2;
+ std::generate_n(std::back_inserter(in1), size, gen);
+ std::generate_n(std::back_inserter(in2), size, gen);
+ DoNotOptimizeData(in1);
+ DoNotOptimizeData(in2);
+
+ Container c(in1.begin(), in1.end());
+ bool toggle = true;
+ for ([[maybe_unused]] auto _ : st) {
+ auto& in = toggle ? in2 : in1;
+ c.assign(Iter(in.begin()), Iter(in.end()));
+ toggle = !toggle;
+ DoNotOptimizeData(c);
+ }
+ });
+ };
+ bm.template operator()<forward_iterator>("fwd_iter");
+ bm.template operator()<random_access_iterator>("ra_iter");
+ }
+ { // Iterator-pair insertion
+ auto bm = [&generators, &bench_vb, &tostr]<template <class> class Iter>(std::string iter) {
+ for (auto gen : generators)
+ bench_vb("insert(begin, " + iter + ", " + iter + ")" + tostr(gen), [gen](auto& st) {
+ auto const size = st.range(0);
+ std::vector<int> in;
+ Container c;
+ std::generate_n(std::back_inserter(in), size, gen);
+ std::generate_n(std::back_inserter(c), size, gen);
+ DoNotOptimizeData(in);
+ DoNotOptimizeData(c);
+
+ for ([[maybe_unused]] auto _ : st) {
+ c.insert(c.begin(), Iter(in.begin()), Iter(in.end()));
+ c.erase(c.begin() + size, c.end()); // avoid growing indefinitely
+ DoNotOptimizeData(c);
+ }
+ });
+ };
+ bm.template operator()<forward_iterator>("fwd_iter");
+ bm.template operator()<random_access_iterator>("ra_iter");
+ }
+
+#if defined(__cpp_lib_containers_ranges) && __cpp_lib_containers_ranges >= 202202L
+ { // Range-ctor
+ auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+ for (auto gen : generators)
+ bench_vb("ctor(" + range + ")" + tostr(gen), [gen](auto& st) {
+ auto const size = st.range(0);
+ std::vector<int> in;
+ std::generate_n(std::back_inserter(in), size, gen);
+ Range rg(std::ranges::begin(in), std::ranges::end(in));
+ benchmark::DoNotOptimize(in);
+
+ for ([[maybe_unused]] auto _ : st) {
+ Container c(std::from_range, rg); // we assume the destructor doesn't dominate the benchmark
+ DoNotOptimizeData(c);
+ }
+ });
+ };
+ bm.template operator()<forward_range_wrapper>("fwd_range");
+ bm.template operator()<random_access_range_wrapper>("ra_range");
+ }
+ { // Range-assignment
+ auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+ for (auto gen : generators)
+ bench_vb("assign_range(" + range + ")" + tostr(gen), [gen](auto& st) {
+ auto const size = st.range(0);
+ std::vector<int> in1, in2;
+ std::generate_n(std::back_inserter(in1), size, gen);
+ std::generate_n(std::back_inserter(in2), size, gen);
+ Range rg1(std::ranges::begin(in1), std::ranges::end(in1));
+ Range rg2(std::ranges::begin(in2), std::ranges::end(in2));
+ DoNotOptimizeData(in1);
+ DoNotOptimizeData(in2);
+
+ Container c(std::from_range, rg1);
+ bool toggle = true;
+ for ([[maybe_unused]] auto _ : st) {
+ auto& in = toggle ? rg2 : rg1;
+ c.assign_range(in);
+ toggle = !toggle;
+ DoNotOptimizeData(c);
+ }
+ });
+ };
+ bm.template operator()<forward_range_wrapper>("fwd_range");
+ bm.template operator()<random_access_range_wrapper>("ra_range");
+ }
+ { // Range-insertion
+ auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+ for (auto gen : generators)
+ bench_vb("insert_range(" + range + ")" + tostr(gen), [gen](auto& st) {
+ auto const size = st.range(0);
+ std::vector<int> in;
+ Container c;
+ std::generate_n(std::back_inserter(in), size, gen);
+ std::generate_n(std::back_inserter(c), size, gen);
+ Range rg(std::ranges::begin(in), std::ranges::end(in));
+ DoNotOptimizeData(in);
+ DoNotOptimizeData(c);
+
+ for ([[maybe_unused]] auto _ : st) {
+ c.insert_range(c.begin(), in);
+ c.erase(c.begin() + size, c.end()); // avoid growing indefinitely
+ DoNotOptimizeData(c);
+ }
+ });
+ };
+ bm.template operator()<forward_range_wrapper>("fwd_range");
+ bm.template operator()<random_access_range_wrapper>("ra_range");
+ }
+ { // Range-append
+ auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+ for (auto gen : generators)
+ bench_vb("append_range(" + range + ")" + tostr(gen), [gen](auto& st) {
+ auto const size = st.range(0);
+ std::vector<int> in;
+ std::generate_n(std::back_inserter(in), size, gen);
+ Range rg(std::ranges::begin(in), std::ranges::end(in));
+ DoNotOptimizeData(in);
+
+ Container c;
+ for ([[maybe_unused]] auto _ : st) {
+ c.append_range(in);
+ c.erase(c.begin(), c.end()); // avoid growing indefinitely
+ DoNotOptimizeData(c);
+ }
+ });
+ };
+ bm.template operator()<forward_range_wrapper>("fwd_range");
+ bm.template operator()<random_access_range_wrapper>("ra_range");
+ }
+#endif
+ }
}
} // namespace support
diff --git a/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp b/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
index 599db1d90fa9a..fb0b3272689ea 100644
--- a/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
+++ b/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
@@ -17,6 +17,7 @@
int main(int argc, char** argv) {
support::sequence_container_benchmarks<std::vector<int>>("std::vector<int>");
support::sequence_container_benchmarks<std::vector<std::string>>("std::vector<std::string>");
+ support::sequence_container_benchmarks<std::vector<bool>>("std::vector<bool>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
deleted file mode 100644
index a434bdbbc6a3e..0000000000000
--- a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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, c++20
-
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <deque>
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "ContainerBenchmarks.h"
-#include "../../std/containers/from_range_helpers.h"
-#include "../GenerateInput.h"
-#include "test_iterators.h"
-
-using namespace ContainerBenchmarks;
-
-using vb_iter = std::vector<bool>::iterator;
-
-// Benchmarks for forward_iterator or forward_range
-
-BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
- forward_iterator,
- getRandomIntegerInputs<bool>,
- forward_iterator<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
- forward_range,
- getRandomIntegerInputs<bool>,
- forward_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
- BM_AssignIterIter<std::vector<bool>>, forward_iterator, getRandomIntegerInputs<bool>, forward_iterator<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
- BM_AssignRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
- forward_iterator,
- getRandomIntegerInputs<bool>,
- forward_iterator<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
- BM_InsertRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
- BM_AppendRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-// Benchmarks for random_access_iterator or random_access_range
-
-BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
- random_access_iterator,
- getRandomIntegerInputs<bool>,
- random_access_iterator<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
- random_access_range,
- getRandomIntegerInputs<bool>,
- random_access_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_AssignIterIter<std::vector<bool>>,
- random_access_iterator,
- getRandomIntegerInputs<bool>,
- random_access_iterator<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_AssignRange<std::vector<bool>>,
- random_access_range,
- getRandomIntegerInputs<bool>,
- random_access_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
- random_access_iterator,
- getRandomIntegerInputs<bool>,
- random_access_iterator<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_InsertRange<std::vector<bool>>,
- random_access_range,
- getRandomIntegerInputs<bool>,
- random_access_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_AppendRange<std::vector<bool>>,
- random_access_range,
- getRandomIntegerInputs<bool>,
- random_access_range_wrapper<vb_iter>())
- ->Arg(5140480);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
index 10909b36b0b4a..5afd20e101511 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
@@ -17,6 +17,8 @@
#include <algorithm>
#include <array>
#include <cassert>
+#include <deque>
+#include <ranges>
#include <vector>
#include "sized_allocator.h"
@@ -65,30 +67,6 @@ struct TestInIters {
}
};
-template <std::size_t N>
-struct CopyFromForwardIterToBitIter {
- std::array<bool, N> in = {};
- template <class FwdIter>
- TEST_CONSTEXPR_CXX20 void operator()() {
- for (std::size_t i = 0; i < in.size(); i += 2)
- in[i] = true;
-
- { // Test with full bytes
- std::vector<bool> out(N);
- std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin());
- for (std::size_t i = 0; i < N; ++i)
- assert(out[i] == static_cast<bool>(in[i]));
- }
- { // Test with partial bytes in both front and back
- std::vector<bool> out(N + 8);
- std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin() + 4);
- for (std::size_t i = 0; i < N; ++i)
- assert(out[i + 4] == static_cast<bool>(in[i]));
- }
- }
-};
-
-
TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
std::vector<bool> in(N, false);
for (std::size_t i = 0; i < N; i += 2)
@@ -109,6 +87,30 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
return true;
}
+template <std::size_t N>
+struct CopyFromForwardIterToBitIter {
+ std::array<bool, N> in;
+
+ template <class FwdIter>
+ TEST_CONSTEXPR_CXX20 void operator()() {
+ for (std::size_t i = 0; i < in.size(); i += 2)
+ in[i] = true;
+
+ { // Aligned
+ std::vector<bool> out(N);
+ std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin());
+ for (std::size_t i = 0; i < N; ++i)
+ assert(out[i] == static_cast<bool>(in[i]));
+ }
+ { // Unaligned
+ std::vector<bool> out(N + 8);
+ std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin() + 4);
+ for (std::size_t i = 0; i < N; ++i)
+ assert(out[i + 4] == static_cast<bool>(in[i]));
+ }
+ }
+};
+
TEST_CONSTEXPR_CXX20 bool test() {
types::for_each(types::cpp17_input_iterator_list<const int*>(), TestInIters());
@@ -284,7 +286,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
}
}
- { // Test std::copy() with forward_iterator-pair inputs and vector<bool>::iterator output
+ { // Test std::copy when copying from forward_iterators (and above) to vector<bool> iterator
types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<8>());
types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<19>());
types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<32>());
@@ -292,6 +294,82 @@ TEST_CONSTEXPR_CXX20 bool test() {
types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<299>());
}
+ // Test std::copy with segmented iterators: deque<T>::iterator, join_view::iterator
+ {
+ // std::deque iterator
+ if (!TEST_IS_CONSTANT_EVALUATED) { // TODO: enable this for constant evaluation when std::deque is fully constexpr
+ { // Copy from segmented input to contiguous output (deque<int> to vector<int>)
+ std::deque<int> in(20);
+ for (std::size_t i = 0; i < in.size(); ++i)
+ in[i] = i;
+ std::vector<int> out(in.size());
+ std::copy(in.begin(), in.end(), out.begin());
+ assert(std::equal(in.begin(), in.end(), out.begin()));
+ }
+ { // Copy from contiguous input to segmented output (vector<int> to deque<int>)
+ std::vector<int> in(20);
+ for (std::size_t i = 0; i < in.size(); ++i)
+ in[i] = i;
+ std::deque<int> out(in.size());
+ std::copy(in.begin(), in.end(), out.begin());
+ assert(std::equal(in.begin(), in.end(), out.begin()));
+ }
+ { // Copy from segmented input to segmented output (deque<int> to deque<int>)
+ std::deque<int> in(20);
+ for (std::size_t i = 0; i < in.size(); ++i)
+ in[i] = i;
+ std::deque<int> out(in.size());
+ std::copy(in.begin(), in.end(), out.begin());
+ assert(in == out);
+ }
+ { // Copy from segmented input to vector<bool> output
+ std::deque<bool> in(199, false);
+ for (std::size_t i = 0; i < in.size(); i += 2)
+ in[i] = true;
+ std::vector<bool> out(in.size());
+ std::copy(in.begin(), in.end(), out.begin());
+ assert(std::equal(in.begin(), in.end(), out.begin()));
+ }
+ { // Copy from vector<bool> input to segmented output
+ std::vector<bool> in(199, false);
+ for (std::size_t i = 0; i < in.size(); i += 2)
+ in[i] = true;
+ std::deque<bool> out(in.size());
+ std::copy(in.begin(), in.end(), out.begin());
+ assert(std::equal(in.begin(), in.end(), out.begin()));
+ }
+ }
+
+#if TEST_STD_VER >= 20
+ // join_view iterator
+ { // Copy from segmented input to contiguous output (join_viw to vector<int>)
+ std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
+ auto jv = std::ranges::join_view(v);
+ std::vector<int> expected(jv.begin(), jv.end());
+ std::vector<int> out(expected.size());
+ std::copy(jv.begin(), jv.end(), out.begin());
+ assert(out == expected);
+ }
+ // Copy from segmented input to segmented output (join_view to deque)
+ if (!TEST_IS_CONSTANT_EVALUATED) { // TODO: enable this for constant evaluation when std::deque is fully constexpr
+ std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
+ auto jv = std::ranges::join_view(v);
+ std::deque<int> expected(jv.begin(), jv.end());
+ std::deque<int> out(expected.size());
+ std::copy(jv.begin(), jv.end(), out.begin());
+ assert(out == expected);
+ }
+ { // Copy from segmented input to vector<bool> output
+ std::vector<std::vector<int>> v{{1, 1}, {1, 0, 1}, {0, 0}, {1, 1, 1}, {1}, {1, 1, 1, 1}, {0, 0, 1, 1, 0, 1, 1}};
+ auto jv = std::ranges::join_view(v);
+ std::vector<bool> expected(jv.begin(), jv.end());
+ std::vector<bool> out(expected.size());
+ std::copy(jv.begin(), jv.end(), out.begin());
+ assert(out == expected);
+ }
+#endif
+ }
+
return true;
}
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index 3b703dbe4d9b7..cf248c87fff43 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -50,7 +50,7 @@ constexpr auto wrap_input(std::vector<T>& input) {
return std::ranges::subrange(std::move(b), std::move(e));
}
-template <class Iter, class Sent = Iter>
+template <class Iter, class Sent>
class forward_range_wrapper {
public:
using _Iter = forward_iterator<Iter>;
@@ -69,7 +69,7 @@ class forward_range_wrapper {
template <class Iter, class Sent>
forward_range_wrapper(Iter, Sent) -> forward_range_wrapper<Iter, Sent>;
-template <class Iter, class Sent = Iter>
+template <class Iter, class Sent>
class random_access_range_wrapper {
public:
using _Iter = cpp20_random_access_iterator<Iter>;
More information about the libcxx-commits
mailing list