Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libcxx/docs/ReleaseNotes/20.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ 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>>`).

Deprecations and Removals
-------------------------

Expand Down
12 changes: 9 additions & 3 deletions libcxx/include/__vector/vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -1003,9 +1003,15 @@ template <class _Tp, class _Allocator>
template <class _Iterator, class _Sentinel>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
vector<_Tp, _Allocator>::__assign_with_sentinel(_Iterator __first, _Sentinel __last) {
clear();
for (; __first != __last; ++__first)
emplace_back(*__first);
pointer __cur = __begin_;
for (; __first != __last && __cur != __end_; ++__first, (void)++__cur)
*__cur = *__first;
if (__cur != __end_) {
__destruct_at_end(__cur);
} else {
for (; __first != __last; ++__first)
emplace_back(*__first);
}
}

template <class _Tp, class _Allocator>
Expand Down
16 changes: 15 additions & 1 deletion libcxx/test/benchmarks/ContainerBenchmarks.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
#include <iterator>
#include <utility>

#include "Utilities.h"
#include "benchmark/benchmark.h"
#include "Utilities.h"
#include "test_iterators.h"

namespace ContainerBenchmarks {

Expand Down Expand Up @@ -50,6 +51,19 @@ void BM_Assignment(benchmark::State& st, Container) {
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not attached to this line: Can you please add a release note to 20.rst mentioning this optimization?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your positive feedback and recognition of my work! I appreciate your time and effort in reviewing this PR. I have added a description of this performance optimization to the release notes and rebased the PR onto the main branch. Thanks again for your help and support!

}

template <std::size_t... sz, typename Container, typename GenInputs>
void BM_AssignInputIterIter(benchmark::State& st, Container c, GenInputs gen) {
auto v = gen(1, sz...);
c.resize(st.range(0), v[0]);
auto in = gen(st.range(1), sz...);
benchmark::DoNotOptimize(&in);
benchmark::DoNotOptimize(&c);
for (auto _ : st) {
c.assign(cpp17_input_iterator(in.begin()), cpp17_input_iterator(in.end()));
benchmark::ClobberMemory();
}
}

template <class Container>
void BM_ConstructSizeValue(benchmark::State& st, Container, typename Container::value_type const& val) {
const auto size = st.range(0);
Expand Down
17 changes: 15 additions & 2 deletions libcxx/test/benchmarks/GenerateInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,29 @@ std::vector<IntT> getRandomIntegerInputs(std::size_t N) {
return inputs;
}

inline std::vector<std::string> getRandomStringInputsWithLength(std::size_t N, std::size_t len) { // N-by-len
std::vector<std::string> inputs;
inputs.reserve(N);
for (std::size_t i = 0; i < N; ++i)
inputs.push_back(getRandomString(len));
return inputs;
}

inline std::vector<std::string> getDuplicateStringInputs(std::size_t N) {
std::vector<std::string> inputs(N, getRandomString(1024));
return inputs;
}

inline std::vector<std::string> getRandomStringInputs(std::size_t N) {
std::vector<std::string> inputs;
return getRandomStringInputsWithLength(N, 1024);
}

template <class IntT>
std::vector<std::vector<IntT>> getRandomIntegerInputsWithLength(std::size_t N, std::size_t len) { // N-by-len
std::vector<std::vector<IntT>> inputs;
inputs.reserve(N);
for (std::size_t i = 0; i < N; ++i)
inputs.push_back(getRandomString(1024));
inputs.push_back(getRandomIntegerInputs<IntT>(len));
return inputs;
}

Expand Down
14 changes: 13 additions & 1 deletion libcxx/test/benchmarks/vector_operations.bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <vector>

#include "benchmark/benchmark.h"

#include "ContainerBenchmarks.h"
#include "GenerateInput.h"

Expand Down Expand Up @@ -79,4 +78,17 @@ BENCHMARK(bm_grow<std::string>);
BENCHMARK(bm_grow<std::unique_ptr<int>>);
BENCHMARK(bm_grow<std::deque<int>>);

BENCHMARK_CAPTURE(BM_AssignInputIterIter, vector_int, std::vector<int>{}, getRandomIntegerInputs<int>)
->Args({TestNumInputs, TestNumInputs});

BENCHMARK_CAPTURE(
BM_AssignInputIterIter<32>, vector_string, std::vector<std::string>{}, getRandomStringInputsWithLength)
->Args({TestNumInputs, TestNumInputs});

BENCHMARK_CAPTURE(BM_AssignInputIterIter<100>,
vector_vector_int,
std::vector<std::vector<int>>{},
getRandomIntegerInputsWithLength<int>)
->Args({TestNumInputs, TestNumInputs});

BENCHMARK_MAIN();