* include/bits/stl_vector.h (vector::_S_insert_aux_assign): Define new overloaded functions. * include/bits/vector.tcc (vector::_M_insert_aux): Use new functions to avoid creating a redundant temporary. * testsuite/23_containers/vector/modifiers/insert_vs_emplace.cc: New test.
Tested x86_64-linux. This improves our performance on Howard Hinnant's "insert vs emplace" experiment at http://htmlpreview.github.io/?https://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html With this small change there is no difference between emplacing or using the relevant insert / push_back function. That also means we beat libc++ in some cases, making us the bestest, whoo! I originally wrote _S_insert_aux_arg functions which returned their argument (either _Tp or _Tp&& as appropriate), relying on RVO to elide the extra constructions, but that caused 23_containers/vector/40192.cc to FAIL, so this patch passes the iterator into the new functions and the assignment is done there. Does anyone see any problem with this optimisation? I'm pretty sure there are no cases where we actually need to create a temporary from an expression that is already an rvalue of the correct type. Howard's test code is CC BY 4.0, so I didn't add our usual GPL header to the test file. I think the comments I added to the file meet the requirements for attribution and indicating changes.
commit 22b2f2460e5508f2c993b41e31add9b10fb2794c Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue Jul 29 10:10:14 2014 +0100 Optimize inserting value_type into std::vector * include/bits/stl_vector.h (vector::_S_insert_aux_assign): Define new overloaded functions. * include/bits/vector.tcc (vector::_M_insert_aux): Use new functions to avoid creating a redundant temporary. * testsuite/23_containers/vector/modifiers/insert_vs_emplace.cc: New test. diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h index 9b6d258..ec1e884 100644 --- a/libstdc++-v3/include/bits/stl_vector.h +++ b/libstdc++-v3/include/bits/stl_vector.h @@ -1407,6 +1407,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _M_insert_aux(iterator __position, const value_type& __x); #else template<typename... _Args> + static void + _S_insert_aux_assign(iterator __pos, _Args&&... __args) + { *__pos = _Tp(std::forward<_Args>(__args)...); } + + static void + _S_insert_aux_assign(iterator __pos, _Tp&& __arg) + { *__pos = std::move(__arg); } + + template<typename... _Args> void _M_insert_aux(iterator __position, _Args&&... __args); diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc index 715b83e..d621804 100644 --- a/libstdc++-v3/include/bits/vector.tcc +++ b/libstdc++-v3/include/bits/vector.tcc @@ -342,7 +342,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER #if __cplusplus < 201103L *__position = __x_copy; #else - *__position = _Tp(std::forward<_Args>(__args)...); + _S_insert_aux_assign(__position, std::forward<_Args>(__args)...); #endif } else diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert_vs_emplace.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert_vs_emplace.cc new file mode 100644 index 0000000..39a3f03 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert_vs_emplace.cc @@ -0,0 +1,573 @@ +// { dg-options "-std=gnu++11" } + +// The class X and test code is by by Howard Hinnant and used under a +// Creative Commons Attribution 4.0 International License. +// http://creativecommons.org/licenses/by/4.0/ +// https://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html +// +// The original code was reformatted and modified to use the VERIFY macro +// instead of writing to standard output. + +#include <testsuite_hooks.h> +#include <vector> +#include <iostream> + +class X +{ + int i_; + int* p_; + +public: + struct special + { + unsigned c; + unsigned dt; + unsigned cc; + unsigned ca; + unsigned mc; + unsigned ma; + }; + static special sp; + + X(int i, int* p) + : i_(i) + , p_(p) + { + // std::cout << "X(int i, int* p)\n"; + sp.c++; + } + + ~X() + { + // std::cout << "~X()\n"; + sp.dt++; + } + + X(const X& x) + : i_(x.i_) + , p_(x.p_) + { + // std::cout << "X(const X& x)\n"; + sp.cc++; + } + + X& operator=(const X& x) + { + + i_ = x.i_; + p_ = x.p_; + // std::cout << "X& operator=(const X& x)\n"; + sp.ca++; + return *this; + } + + X(X&& x) noexcept + : i_(x.i_) + , p_(x.p_) + { + // std::cout << "X(X&& x)\n"; + sp.mc++; + } + + X& operator=(X&& x) noexcept + { + + i_ = x.i_; + p_ = x.p_; + // std::cout << "X& operator=(X&& x)\n"; + sp.ma++; + return *this; + } + +}; + +std::ostream& +operator<<(std::ostream& os, X::special const& sp) +{ + os << sp.c << '\n'; + os << sp.dt << '\n'; + os << sp.cc << '\n'; + os << sp.ca << '\n'; + os << sp.mc << '\n'; + os << sp.ma << '\n'; + return os; +} + +X::special X::sp{}; + +bool +operator==(const X::special& lhs, const X::special& rhs) +{ + return lhs.c == rhs.c && lhs.dt == rhs.dt + && lhs.cc == rhs.cc && lhs.ca == rhs.ca + && lhs.mc == rhs.mc && lhs.ma == rhs.ma; +} + +// Verify that insert and emplace are equally efficient. +// Also verify exact number of operations (which are specific to this +// implementation) in order to catch any regressions. + +// insert vs emplace lvalue no reallocation +void +test01() +{ + const X::special expected{ 0, 1, 1, 0, 1, 3 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--insert lvalue no reallocation--\n"; + X::sp = {}; + v.insert(v.begin(), x); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace lvalue no reallocation--\n"; + X::sp = {}; + v.emplace(v.begin(), x); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// insert vs emplace xvalue no reallocation +void +test02() +{ + const X::special expected{ 0, 0, 0, 0, 1, 3 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--insert xvalue no reallocation--\n"; + X::sp = {}; + v.insert(v.begin(), std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace xvalue no reallocation--\n"; + X::sp = {}; + v.emplace(v.begin(), std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// insert vs emplace rvalue no reallocation +void +test03() +{ + const X::special expected{ 1, 1, 0, 0, 1, 3 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--insert rvalue no reallocation--\n"; + X::sp = {}; + v.insert(v.begin(), X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--emplace rvalue no reallocation--\n"; + X::sp = {}; + v.emplace(v.begin(), X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// insert vs emplace lvalue reallocation +void +test04() +{ + const X::special expected{ 0, 3, 1, 0, 3, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--insert lvalue reallocation--\n"; + X::sp = {}; + v.insert(v.begin(), x); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace lvalue reallocation--\n"; + X::sp = {}; + v.emplace(v.begin(), x); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// insert vs emplace xvalue reallocation +void +test05() +{ + const X::special expected{ 0, 3, 0, 0, 4, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--insert xvalue reallocation--\n"; + X::sp = {}; + v.insert(v.begin(), std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace xvalue reallocation--\n"; + X::sp = {}; + v.emplace(v.begin(), std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// insert vs emplace rvalue reallocation +void +test06() +{ + const X::special expected{ 1, 4, 0, 0, 4, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--insert rvalue reallocation--\n"; + X::sp = {}; + v.insert(v.begin(), X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--emplace rvalue reallocation--\n"; + X::sp = {}; + v.emplace(v.begin(), X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// push_back vs emplace_back lvalue no reallocation +void +test07() +{ + const X::special expected{ 0, 0, 1, 0, 0, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--push_back lvalue no reallocation--\n"; + X::sp = {}; + v.push_back(x); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace_back lvalue no reallocation--\n"; + X::sp = {}; + v.emplace_back(x); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// push_back vs emplace_back xvalue no reallocation +void +test08() +{ + const X::special expected{ 0, 0, 0, 0, 1, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--push_back xvalue no reallocation--\n"; + X::sp = {}; + v.push_back(std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace_back xvalue no reallocation--\n"; + X::sp = {}; + v.emplace_back(std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// push_back vs emplace_back rvalue no reallocation +void +test09() +{ + const X::special expected{ 1, 1, 0, 0, 1, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--push_back rvalue no reallocation--\n"; + X::sp = {}; + v.push_back(X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(4); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--emplace_back rvalue no reallocation--\n"; + X::sp = {}; + v.emplace_back(X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// push_back vs emplace_back lvalue reallocation +void +test10() +{ + const X::special expected{ 0, 3, 1, 0, 3, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--push_back lvalue reallocation--\n"; + X::sp = {}; + v.push_back(x); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace_back lvalue reallocation--\n"; + X::sp = {}; + v.emplace_back(x); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// push_back vs emplace_back xvalue reallocation +void +test11() +{ + const X::special expected{ 0, 3, 0, 0, 4, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--push_back xvalue reallocation--\n"; + X::sp = {}; + v.push_back(std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + X x{0,0}; + // std::cout << "--emplace_back xvalue reallocation--\n"; + X::sp = {}; + v.emplace_back(std::move(x)); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +// push_back vs emplace_back rvalue reallocation +void +test12() +{ + const X::special expected{ 1, 4, 0, 0, 4, 0 }; + X::special ins, emp; + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--push_back rvalue reallocation--\n"; + X::sp = {}; + v.push_back(X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + ins = X::sp; + } + { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + v.push_back(X(0,0)); + // std::cout << "--emplace_back rvalue reallocation--\n"; + X::sp = {}; + v.emplace_back(X{0,0}); + // std::cout << X::sp; + // std::cout << "----\n"; + emp = X::sp; + } + VERIFY( ins == emp ); + VERIFY( ins == expected ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); + test08(); + test09(); + test10(); + test11(); + test12(); +}