diff --git a/libcxx/benchmarks/map.bench.cpp b/libcxx/benchmarks/map.bench.cpp --- a/libcxx/benchmarks/map.bench.cpp +++ b/libcxx/benchmarks/map.bench.cpp @@ -168,6 +168,29 @@ std::string name() const { return "BM_ConstructorMove" + baseName(); } }; +struct CopyAssignment : Base { + using Base::Base; + + void run(benchmark::State& State) const { + auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1); + auto& Map = Data.Maps.front(); + while (State.KeepRunningBatch(MapSize)) { +#ifndef VALIDATE + std::map M; + M = Map; + benchmark::DoNotOptimize(M); +#else + std::map M; + M = Map; + if (M != Map) + State.SkipWithError("Map copy assignment not identical"); +#endif + } + } + + std::string name() const { return "BM_CopyAssignment" + baseName(); } +}; + //*******************************************************************| // Capacity | //*******************************************************************| @@ -1006,6 +1029,7 @@ makeCartesianProductBenchmark(MapSize); makeCartesianProductBenchmark(MapSize); makeCartesianProductBenchmark(MapSize); + makeCartesianProductBenchmark(MapSize); // Capacity makeCartesianProductBenchmark(MapSize); diff --git a/libcxx/include/__tree b/libcxx/include/__tree --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -1095,7 +1095,6 @@ explicit __tree(const allocator_type& __a); __tree(const value_compare& __comp, const allocator_type& __a); __tree(const __tree& __t); - __tree& operator=(const __tree& __t); template void __assign_unique(_ForwardIterator __first, _ForwardIterator __last); template @@ -1534,6 +1533,8 @@ template friend class _LIBCPP_TEMPLATE_VIS map; template friend class _LIBCPP_TEMPLATE_VIS multimap; + template friend class _LIBCPP_TEMPLATE_VIS set; + template friend class _LIBCPP_TEMPLATE_VIS multiset; }; template @@ -1609,19 +1610,6 @@ return static_cast<__node_pointer>(_VSTD::__tree_leaf(__cache->__left_)); } -template -__tree<_Tp, _Compare, _Allocator>& -__tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) -{ - if (this != _VSTD::addressof(__t)) - { - value_comp() = __t.value_comp(); - __copy_assign_alloc(__t); - __assign_multi(__t.begin(), __t.end()); - } - return *this; -} - template template void diff --git a/libcxx/include/map b/libcxx/include/map --- a/libcxx/include/map +++ b/libcxx/include/map @@ -1077,16 +1077,12 @@ _LIBCPP_INLINE_VISIBILITY map& operator=(const map& __m) { -#ifndef _LIBCPP_CXX03_LANG - __tree_ = __m.__tree_; -#else if (this != _VSTD::addressof(__m)) { __tree_.clear(); __tree_.value_comp() = __m.__tree_.value_comp(); __tree_.__copy_assign_alloc(__m.__tree_); insert(__m.begin(), __m.end()); } -#endif return *this; } @@ -1863,16 +1859,12 @@ _LIBCPP_INLINE_VISIBILITY multimap& operator=(const multimap& __m) { -#ifndef _LIBCPP_CXX03_LANG - __tree_ = __m.__tree_; -#else if (this != _VSTD::addressof(__m)) { __tree_.clear(); __tree_.value_comp() = __m.__tree_.value_comp(); __tree_.__copy_assign_alloc(__m.__tree_); insert(__m.begin(), __m.end()); } -#endif return *this; } diff --git a/libcxx/include/set b/libcxx/include/set --- a/libcxx/include/set +++ b/libcxx/include/set @@ -591,7 +591,12 @@ _LIBCPP_INLINE_VISIBILITY set& operator=(const set& __s) { - __tree_ = __s.__tree_; + if (this != _VSTD::addressof(__s)) { + __tree_.clear(); + __tree_.value_comp() = __s.__tree_.value_comp(); + __tree_.__copy_assign_alloc(__s.__tree_); + insert(__s.begin(), __s.end()); + } return *this; } @@ -1125,7 +1130,12 @@ _LIBCPP_INLINE_VISIBILITY multiset& operator=(const multiset& __s) { - __tree_ = __s.__tree_; + if (this != _VSTD::addressof(__s)) { + __tree_.clear(); + __tree_.value_comp() = __s.__tree_.value_comp(); + __tree_.__copy_assign_alloc(__s.__tree_); + insert(__s.begin(), __s.end()); + } return *this; } diff --git a/libcxx/test/libcxx/containers/associative/perf.assignment_vs_construction.pass.cpp b/libcxx/test/libcxx/containers/associative/perf.assignment_vs_construction.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/containers/associative/perf.assignment_vs_construction.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// This test ensures that copy assignment and copy construction are within a reasonable +// margin from each other performance wise. + +#include +#include +#include +#include +#include +#include +#include +#include + +std::vector generate_keys(int N) { + auto generator = std::default_random_engine(12345); + + // Generate random keys to store in the container + std::vector keys; + for (int i = 0; i != N; ++i) { + auto key = std::to_string(generator()); + keys.push_back(key); + } + + // Make sure all the strings are unique + std::set tmp(keys.begin(), keys.end()); + keys.assign(tmp.begin(), tmp.end()); + + std::shuffle(keys.begin(), keys.end(), generator); + return keys; +} + +template +std::size_t microseconds_avg(std::size_t iterations, F const& f) { + std::vector durations; + for (size_t i = 0; i < iterations; ++i) { + const auto start = std::chrono::high_resolution_clock::now(); + f(); + const auto stop = std::chrono::high_resolution_clock::now(); + const auto duration = std::chrono::duration_cast(stop - start).count(); + durations.push_back(duration); + } + + return std::accumulate(durations.begin(), durations.end(), 0) / iterations; +} + +template +void test_map(std::vector const& keys) { + constexpr std::size_t iterations = 5; + + Container tmp; + for (const auto& key : keys) + tmp.insert({key, 1}); + + std::size_t ctor_avg = microseconds_avg(iterations, [&] { + Container foo(tmp); + }); + + std::size_t assignment_avg = microseconds_avg(iterations, [&] { + Container foo; + foo = tmp; + }); + + auto percentage = (std::labs((ssize_t)ctor_avg - (ssize_t)assignment_avg) / (double)ctor_avg) * 100.0; + assert(percentage < 10.0 && "Copy assignment and copy constructor have more than 10% difference"); +} + +template +void test_set(std::vector const& keys) { + constexpr std::size_t iterations = 5; + + Container tmp; + for (const auto& key : keys) + tmp.insert(key); + + std::size_t ctor_avg = microseconds_avg(iterations, [&] { + Container foo(tmp); + }); + + std::size_t assignment_avg = microseconds_avg(iterations, [&] { + Container foo; + foo = tmp; + }); + + auto percentage = (std::labs((ssize_t)ctor_avg - (ssize_t)assignment_avg) / (double)ctor_avg) * 100.0; + assert(percentage < 10.0 && "Copy assignment and copy constructor have more than 10% difference"); +} + +int main(int, char const**) { + std::vector keys = generate_keys(1000000); + test_map>(keys); + test_map>(keys); + + test_set>(keys); + test_set>(keys); + return 0; +}