diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst index 93d45f508d5ff..a72fd2d5db9e2 100644 --- a/libcxx/docs/ReleaseNotes/20.rst +++ b/libcxx/docs/ReleaseNotes/20.rst @@ -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`), + and up to 3.4x for non-trivial element types (e.g., `std::vector>`). + Deprecations and Removals ------------------------- diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h index 4b9ba24e97f65..6ba7ba7bcf724 100644 --- a/libcxx/include/__vector/vector.h +++ b/libcxx/include/__vector/vector.h @@ -1003,9 +1003,15 @@ template template _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 diff --git a/libcxx/test/benchmarks/ContainerBenchmarks.h b/libcxx/test/benchmarks/ContainerBenchmarks.h index 38e11777f488b..458134c8191d1 100644 --- a/libcxx/test/benchmarks/ContainerBenchmarks.h +++ b/libcxx/test/benchmarks/ContainerBenchmarks.h @@ -14,8 +14,9 @@ #include #include -#include "Utilities.h" #include "benchmark/benchmark.h" +#include "Utilities.h" +#include "test_iterators.h" namespace ContainerBenchmarks { @@ -50,6 +51,19 @@ void BM_Assignment(benchmark::State& st, Container) { } } +template +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 void BM_ConstructSizeValue(benchmark::State& st, Container, typename Container::value_type const& val) { const auto size = st.range(0); diff --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h index db376614382bc..67ce798071eaa 100644 --- a/libcxx/test/benchmarks/GenerateInput.h +++ b/libcxx/test/benchmarks/GenerateInput.h @@ -108,16 +108,29 @@ std::vector getRandomIntegerInputs(std::size_t N) { return inputs; } +inline std::vector getRandomStringInputsWithLength(std::size_t N, std::size_t len) { // N-by-len + std::vector inputs; + inputs.reserve(N); + for (std::size_t i = 0; i < N; ++i) + inputs.push_back(getRandomString(len)); + return inputs; +} + inline std::vector getDuplicateStringInputs(std::size_t N) { std::vector inputs(N, getRandomString(1024)); return inputs; } inline std::vector getRandomStringInputs(std::size_t N) { - std::vector inputs; + return getRandomStringInputsWithLength(N, 1024); +} + +template +std::vector> getRandomIntegerInputsWithLength(std::size_t N, std::size_t len) { // N-by-len + std::vector> inputs; inputs.reserve(N); for (std::size_t i = 0; i < N; ++i) - inputs.push_back(getRandomString(1024)); + inputs.push_back(getRandomIntegerInputs(len)); return inputs; } diff --git a/libcxx/test/benchmarks/vector_operations.bench.cpp b/libcxx/test/benchmarks/vector_operations.bench.cpp index 1855861263324..3a72eaec4dd57 100644 --- a/libcxx/test/benchmarks/vector_operations.bench.cpp +++ b/libcxx/test/benchmarks/vector_operations.bench.cpp @@ -18,7 +18,6 @@ #include #include "benchmark/benchmark.h" - #include "ContainerBenchmarks.h" #include "GenerateInput.h" @@ -79,4 +78,17 @@ BENCHMARK(bm_grow); BENCHMARK(bm_grow>); BENCHMARK(bm_grow>); +BENCHMARK_CAPTURE(BM_AssignInputIterIter, vector_int, std::vector{}, getRandomIntegerInputs) + ->Args({TestNumInputs, TestNumInputs}); + +BENCHMARK_CAPTURE( + BM_AssignInputIterIter<32>, vector_string, std::vector{}, getRandomStringInputsWithLength) + ->Args({TestNumInputs, TestNumInputs}); + +BENCHMARK_CAPTURE(BM_AssignInputIterIter<100>, + vector_vector_int, + std::vector>{}, + getRandomIntegerInputsWithLength) + ->Args({TestNumInputs, TestNumInputs}); + BENCHMARK_MAIN();