[libcxx-commits] [libcxx] Optimize input_iterator-pair `insert` for std::vector (PR #113768)
Peng Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Dec 30 17:09:40 PST 2024
https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/113768
>From 8fd97516a728ca1581116cf2151210d06b36aaef Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sat, 26 Oct 2024 13:35:48 -0400
Subject: [PATCH 1/3] Optimize __insert_with_sentinel Function in std::vector
---
libcxx/include/__vector/vector.h | 43 +++++++++----------
.../insert_iter_iter_iter.pass.cpp | 40 +++++++++++++++++
2 files changed, 61 insertions(+), 22 deletions(-)
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 6ba7ba7bcf724b..f6374ec684107c 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -1250,30 +1250,29 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
difference_type __off = __position - begin();
pointer __p = this->__begin_ + __off;
pointer __old_last = this->__end_;
- for (; this->__end_ != this->__cap_ && __first != __last; ++__first) {
+ for (; this->__end_ != this->__cap_ && __first != __last; ++__first)
__construct_one_at_end(*__first);
+
+ if (__first == __last)
+ (void)std::rotate(__p, __old_last, this->__end_);
+ else {
+ __split_buffer<value_type, allocator_type&> __v(__alloc_);
+ auto __guard = std::__make_exception_guard(
+ _AllocatorDestroyRangeReverse<allocator_type, pointer>(__alloc_, __old_last, this->__end_));
+ __v.__construct_at_end_with_sentinel(std::move(__first), std::move(__last));
+ __split_buffer<value_type, allocator_type&> __merged(__recommend(size() + __v.size()), __off, __alloc_);
+ std::__uninitialized_allocator_relocate(
+ __alloc_, std::__to_address(__old_last), std::__to_address(this->__end_), std::__to_address(__merged.__end_));
+ __guard.__complete(); // Release the guard once objects in [__old_last_, __end_) have been successfully relocated.
+ __merged.__end_ += this->__end_ - __old_last;
+ this->__end_ = __old_last;
+ std::__uninitialized_allocator_relocate(
+ __alloc_, std::__to_address(__v.__begin_), std::__to_address(__v.__end_), std::__to_address(__merged.__end_));
+ __merged.__end_ += __v.size();
+ __v.__end_ = __v.__begin_;
+ __p = __swap_out_circular_buffer(__merged, __p);
}
- __split_buffer<value_type, allocator_type&> __v(this->__alloc_);
- if (__first != __last) {
-#if _LIBCPP_HAS_EXCEPTIONS
- try {
-#endif // _LIBCPP_HAS_EXCEPTIONS
- __v.__construct_at_end_with_sentinel(std::move(__first), std::move(__last));
- difference_type __old_size = __old_last - this->__begin_;
- difference_type __old_p = __p - this->__begin_;
- reserve(__recommend(size() + __v.size()));
- __p = this->__begin_ + __old_p;
- __old_last = this->__begin_ + __old_size;
-#if _LIBCPP_HAS_EXCEPTIONS
- } catch (...) {
- erase(__make_iter(__old_last), end());
- throw;
- }
-#endif // _LIBCPP_HAS_EXCEPTIONS
- }
- __p = std::rotate(__p, __old_last, this->__end_);
- insert(__make_iter(__p), std::make_move_iterator(__v.begin()), std::make_move_iterator(__v.end()));
- return begin() + __off;
+ return __make_iter(__p);
}
template <class _Tp, class _Allocator>
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
index 934b85ce01c67b..8dce6e5c1a690e 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
@@ -46,6 +46,46 @@ TEST_CONSTEXPR_CXX20 bool tests()
for (; j < 105; ++j)
assert(v[j] == 0);
}
+ { // Vector may or may not need to reallocate because of the insertion -- test both cases.
+ { // The input range is shorter than the remaining capacity of the vector -- ensure no reallocation happens.
+ typedef std::vector<int> V;
+ V v(100);
+ v.reserve(v.size() + 10);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i =
+ v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
+ { // The input range is longer than the remaining capacity of the vector -- ensure reallocation happens.
+ typedef std::vector<int> V;
+ V v(100);
+ v.reserve(v.size() + 2);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i =
+ v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
+ }
{
typedef std::vector<int> V;
V v(100);
>From e5d431e15ddf8b3ce6981f3558bfb0e248571bfb Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Thu, 7 Nov 2024 17:09:30 -0500
Subject: [PATCH 2/3] Add benchmark tests
---
libcxx/include/__vector/vector.h | 4 +-
libcxx/test/benchmarks/GenerateInput.h | 9 +-
.../containers/ContainerBenchmarks.h | 60 +++
.../containers/vector_operations.bench.cpp | 14 +
.../insert_iter_iter_iter.pass.cpp | 379 +++++++++---------
5 files changed, 264 insertions(+), 202 deletions(-)
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index f6374ec684107c..3cd325f0ad9437 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -1269,8 +1269,8 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
std::__uninitialized_allocator_relocate(
__alloc_, std::__to_address(__v.__begin_), std::__to_address(__v.__end_), std::__to_address(__merged.__end_));
__merged.__end_ += __v.size();
- __v.__end_ = __v.__begin_;
- __p = __swap_out_circular_buffer(__merged, __p);
+ __v.__end_ = __v.__begin_;
+ __p = __swap_out_circular_buffer(__merged, __p);
}
return __make_iter(__p);
}
diff --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h
index 67ce798071eaa4..6d5c5167e91ed8 100644
--- a/libcxx/test/benchmarks/GenerateInput.h
+++ b/libcxx/test/benchmarks/GenerateInput.h
@@ -134,7 +134,14 @@ std::vector<std::vector<IntT>> getRandomIntegerInputsWithLength(std::size_t N, s
return inputs;
}
-inline std::vector<std::string> getPrefixedRandomStringInputs(std::size_t N) {
+inline std::vector<std::string> getSSORandomStringInputs(size_t N) {
+ std::vector<std::string> inputs;
+ for (size_t i = 0; i < N; ++i)
+ inputs.push_back(getRandomString(10)); // SSO
+ return inputs;
+}
+
+inline std::vector<std::string> getPrefixedRandomStringInputs(size_t N) {
std::vector<std::string> inputs;
inputs.reserve(N);
constexpr int kSuffixLength = 32;
diff --git a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
index 6d21e12896ec9e..5fc8981619672c 100644
--- a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
+++ b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
@@ -135,6 +135,66 @@ void BM_InsertValueRehash(benchmark::State& st, Container c, GenInputs gen) {
}
}
+template <class Container, class GenInputs>
+void BM_Insert_InputIterIter_NoRealloc(benchmark::State& st, Container c, GenInputs gen) {
+ auto in = gen(st.range(0));
+ DoNotOptimizeData(in);
+ const auto size = c.size();
+ const auto beg = cpp17_input_iterator(in.begin());
+ const auto end = cpp17_input_iterator(in.end());
+ c.reserve(size + in.size()); // force no reallocation
+ for (auto _ : st) {
+ benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end)));
+ st.PauseTiming();
+ c.erase(c.begin() + size, c.end()); // avoid the container to grow indefinitely
+ st.ResumeTiming();
+ DoNotOptimizeData(c);
+ benchmark::ClobberMemory();
+ }
+}
+
+template <class Container, class GenInputs>
+void BM_Insert_InputIterIter_Realloc_HalfFilled(benchmark::State& st, Container, GenInputs gen) {
+ const auto size = st.range(0);
+ Container a = gen(size);
+ Container in = gen(size + 10);
+ DoNotOptimizeData(a);
+ DoNotOptimizeData(in);
+ const auto beg = cpp17_input_iterator(in.begin());
+ const auto end = cpp17_input_iterator(in.end());
+ for (auto _ : st) {
+ st.PauseTiming();
+ Container c;
+ c.reserve(size * 2); // Reallocation with half-filled container
+ c = a;
+ st.ResumeTiming();
+ benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end)));
+ DoNotOptimizeData(c);
+ benchmark::ClobberMemory();
+ }
+}
+
+template <class Container, class GenInputs>
+void BM_Insert_InputIterIter_Realloc_NearFull(benchmark::State& st, Container, GenInputs gen) {
+ const auto size = st.range(0);
+ Container a = gen(size);
+ Container in = gen(10);
+ DoNotOptimizeData(a);
+ DoNotOptimizeData(in);
+ const auto beg = cpp17_input_iterator(in.begin());
+ const auto end = cpp17_input_iterator(in.end());
+ for (auto _ : st) {
+ st.PauseTiming();
+ Container c;
+ c.reserve(size + 5); // Reallocation almost-full container
+ c = a;
+ st.ResumeTiming();
+ benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end)));
+ DoNotOptimizeData(c);
+ benchmark::ClobberMemory();
+ }
+}
+
template <class Container, class GenInputs>
void BM_InsertDuplicate(benchmark::State& st, Container c, GenInputs gen) {
auto in = gen(st.range(0));
diff --git a/libcxx/test/benchmarks/containers/vector_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_operations.bench.cpp
index 9449bed31ec38c..1cd754ca7e7803 100644
--- a/libcxx/test/benchmarks/containers/vector_operations.bench.cpp
+++ b/libcxx/test/benchmarks/containers/vector_operations.bench.cpp
@@ -91,4 +91,18 @@ BENCHMARK_CAPTURE(BM_AssignInputIterIter<100>,
getRandomIntegerInputsWithLength<int>)
->Args({TestNumInputs, TestNumInputs});
+BENCHMARK_CAPTURE(BM_Insert_InputIterIter_NoRealloc, vector_int, std::vector<int>(100, 1), getRandomIntegerInputs<int>)
+ ->Arg(514048);
+BENCHMARK_CAPTURE(
+ BM_Insert_InputIterIter_Realloc_HalfFilled, vector_int, std::vector<int>{}, getRandomIntegerInputs<int>)
+ ->Arg(514048);
+BENCHMARK_CAPTURE(BM_Insert_InputIterIter_Realloc_NearFull, vector_int, std::vector<int>{}, getRandomIntegerInputs<int>)
+ ->Arg(514048);
+BENCHMARK_CAPTURE(
+ BM_Insert_InputIterIter_Realloc_HalfFilled, vector_string, std::vector<std::string>{}, getSSORandomStringInputs)
+ ->Arg(514048);
+BENCHMARK_CAPTURE(
+ BM_Insert_InputIterIter_Realloc_NearFull, vector_string, std::vector<std::string>{}, getSSORandomStringInputs)
+ ->Arg(514048);
+
BENCHMARK_MAIN();
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
index 8dce6e5c1a690e..e4625e4865c4dd 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
@@ -24,212 +24,193 @@
namespace adl {
struct S {};
void make_move_iterator(S*) {}
-}
+} // namespace adl
+
+TEST_CONSTEXPR_CXX20 bool tests() {
+ //
+ // Tests for input_iterator
+ // Vector may or may not reallocate during insertion -- test both cases.
+ //
+ { // Test insertion into a vector with less spare space available than the input range, triggering reallocation
+ typedef std::vector<int> V;
+ V v(100);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i =
+ v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
+ { // Test insertion into a vector with sufficient spare space where no reallocation happens
+ typedef std::vector<int> V;
+ V v(100);
+ v.reserve(v.size() + 10);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i =
+ v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
-TEST_CONSTEXPR_CXX20 bool tests()
-{
- {
- typedef std::vector<int> V;
- V v(100);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a),
- cpp17_input_iterator<const int*>(a+N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
- { // Vector may or may not need to reallocate because of the insertion -- test both cases.
- { // The input range is shorter than the remaining capacity of the vector -- ensure no reallocation happens.
- typedef std::vector<int> V;
- V v(100);
- v.reserve(v.size() + 10);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a) / sizeof(a[0]);
- V::iterator i =
- v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
- { // The input range is longer than the remaining capacity of the vector -- ensure reallocation happens.
- typedef std::vector<int> V;
- V v(100);
- v.reserve(v.size() + 2);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a) / sizeof(a[0]);
- V::iterator i =
- v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
- }
- {
- typedef std::vector<int> V;
- V v(100);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
- forward_iterator<const int*>(a+N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
- {
- typedef std::vector<int> V;
- V v(100);
- while(v.size() < v.capacity()) v.push_back(0); // force reallocation
- std::size_t sz = v.size();
- int a[] = {1, 2, 3, 4, 5};
- const unsigned N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
- forward_iterator<const int*>(a+N));
- assert(v.size() == sz + N);
- assert(i == v.begin() + 10);
- std::size_t j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < v.size(); ++j)
- assert(v[j] == 0);
- }
- {
- typedef std::vector<int> V;
- V v(100);
- v.reserve(128); // force no reallocation
- std::size_t sz = v.size();
- int a[] = {1, 2, 3, 4, 5};
- const unsigned N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
- forward_iterator<const int*>(a+N));
- assert(v.size() == sz + N);
- assert(i == v.begin() + 10);
- std::size_t j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < v.size(); ++j)
- assert(v[j] == 0);
- }
- {
- typedef std::vector<int, limited_allocator<int, 308> > V;
- V v(100);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a),
- cpp17_input_iterator<const int*>(a+N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
- {
- typedef std::vector<int, limited_allocator<int, 300> > V;
- V v(100);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
- forward_iterator<const int*>(a+N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
+ //
+ // Tests for forward_iterator
+ //
+ {
+ typedef std::vector<int> V;
+ V v(100);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
+ {
+ typedef std::vector<int> V;
+ V v(100);
+ while (v.size() < v.capacity())
+ v.push_back(0); // force reallocation
+ std::size_t sz = v.size();
+ int a[] = {1, 2, 3, 4, 5};
+ const unsigned N = sizeof(a) / sizeof(a[0]);
+ V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+ assert(v.size() == sz + N);
+ assert(i == v.begin() + 10);
+ std::size_t j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < v.size(); ++j)
+ assert(v[j] == 0);
+ }
+ {
+ typedef std::vector<int> V;
+ V v(100);
+ v.reserve(128); // force no reallocation
+ std::size_t sz = v.size();
+ int a[] = {1, 2, 3, 4, 5};
+ const unsigned N = sizeof(a) / sizeof(a[0]);
+ V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+ assert(v.size() == sz + N);
+ assert(i == v.begin() + 10);
+ std::size_t j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < v.size(); ++j)
+ assert(v[j] == 0);
+ }
+ {
+ typedef std::vector<int, limited_allocator<int, 308> > V;
+ V v(100);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i =
+ v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
+ {
+ typedef std::vector<int, limited_allocator<int, 300> > V;
+ V v(100);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
#if TEST_STD_VER >= 11
- {
- typedef std::vector<int, min_allocator<int> > V;
- V v(100);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a),
- cpp17_input_iterator<const int*>(a+N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
- {
- typedef std::vector<int, min_allocator<int> > V;
- V v(100);
- int a[] = {1, 2, 3, 4, 5};
- const int N = sizeof(a)/sizeof(a[0]);
- V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
- forward_iterator<const int*>(a+N));
- assert(v.size() == 100 + N);
- assert(is_contiguous_container_asan_correct(v));
- assert(i == v.begin() + 10);
- int j;
- for (j = 0; j < 10; ++j)
- assert(v[j] == 0);
- for (std::size_t k = 0; k < N; ++j, ++k)
- assert(v[j] == a[k]);
- for (; j < 105; ++j)
- assert(v[j] == 0);
- }
+ {
+ typedef std::vector<int, min_allocator<int> > V;
+ V v(100);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i =
+ v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
+ {
+ typedef std::vector<int, min_allocator<int> > V;
+ V v(100);
+ int a[] = {1, 2, 3, 4, 5};
+ const int N = sizeof(a) / sizeof(a[0]);
+ V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+ assert(v.size() == 100 + N);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ for (std::size_t k = 0; k < N; ++j, ++k)
+ assert(v[j] == a[k]);
+ for (; j < 105; ++j)
+ assert(v[j] == 0);
+ }
#endif
- {
- std::vector<adl::S> s;
- s.insert(s.end(), cpp17_input_iterator<adl::S*>(nullptr), cpp17_input_iterator<adl::S*>(nullptr));
- }
+ {
+ std::vector<adl::S> s;
+ s.insert(s.end(), cpp17_input_iterator<adl::S*>(nullptr), cpp17_input_iterator<adl::S*>(nullptr));
+ }
- return true;
+ 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;
}
>From d57c222d34443eff60083c3859b88b333d04afce Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Mon, 30 Dec 2024 15:51:40 -0500
Subject: [PATCH 3/3] Update release note entry
---
libcxx/docs/ReleaseNotes/20.rst | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index c8a07fb8b73348..e4d64ff4799265 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -69,9 +69,13 @@ Improvements and New Features
- The ``_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY`` ABI configuration was added, which allows storing valid bounds
in ``std::array::iterator`` and detecting OOB accesses when the appropriate hardening mode is enabled.
-- The input iterator overload of `assign(_InputIterator, _InputIterator)` in `std::vector<_Tp, _Allocator>` has been
- optimized, resulting in a performance improvement of up to 2x for trivial element types (e.g., `std::vector<int>`),
- and up to 3.4x for non-trivial element types (e.g., `std::vector<std::vector<int>>`).
+- The ``input_iterator``-pair overload of ``void assign(InputIt, InputIt)`` has been optimized for `std::vector`,
+ resulting in a performance improvement of up to 2x for trivial element types (e.g., ``std::vector<int>``), and up
+ to 3.4x for non-trivial element types (e.g., ``std::vector<std::vector<int>>``).
+
+- The ``input_iterator``-pair overload of ``iterator insert(const_iterator, InputIt, InputIt)`` has been optimized
+ for ``std::vector``, resulting in a performance improvement of up to 10x for `std::vector<int>`, and up to 2.3x
+ for ``std::vector<std::vector<int>>``.
Deprecations and Removals
-------------------------
More information about the libcxx-commits
mailing list