diff --git a/bench/bench_insert_vs_emplace.cpp b/bench/bench_insert_vs_emplace.cpp new file mode 100644 index 0000000..05009d1 --- /dev/null +++ b/bench/bench_insert_vs_emplace.cpp @@ -0,0 +1,467 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Howard Hinnant 2014. +// (C) Copyright Ion Gaztanaga 2014-2014. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// +// +// This testcase is based on H. Hinnant's article "Insert vs. Emplace", +// (http://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html) +// +////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +class X +{ + int i_; + int* p_; + + BOOST_COPYABLE_AND_MOVABLE(X) + +public: + struct special + { + unsigned c; + unsigned dt; + unsigned cc; + unsigned ca; + unsigned mc; + unsigned ma; + + friend bool operator==(const special &l, const special &r) + { return l.c == r.c && l.dt == r.dt && l.cc == r.cc && l.ca == r.ca && l.mc == r.mc && l.ma == r.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=(BOOST_COPY_ASSIGN_REF(X) x) + { + + i_ = x.i_; + p_ = x.p_; +// std::cout << "X& operator=(const X& x)\n"; + sp.ca++; + return *this; + } + + X(BOOST_RV_REF(X) x) BOOST_CONTAINER_NOEXCEPT + : i_(x.i_) + , p_(x.p_) + { +// std::cout << "X(X&& x)\n"; + sp.mc++; + } + + X& operator=(BOOST_RV_REF(X) x) BOOST_CONTAINER_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 << "Const: " << sp.c << '\n'; + os << "Destr: " << sp.dt << '\n'; + os << "CopyC: " << sp.cc << '\n'; + os << "CopyA: " << sp.ca << '\n'; + os << "MoveC: " << sp.mc << '\n'; + os << "MoveA: " << sp.ma << '\n'; + os << "Total: " << (sp.c + sp.dt + sp.cc + sp.ca + sp.mc + sp.ma) << '\n'; + return os; +} + +X::special X::sp = X::special(); + +int +main() +{ + X::special insert_results; + X::special emplace_results; + { + boost::container::vector 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 = X::special(); + v.insert(v.begin(), x); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace(v.begin(), x); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.insert(v.begin(), boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace(v.begin(), boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + BOOST_TEST_EQ(insert_results == emplace_results, true); + { + boost::container::vector 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 = X::special(); + v.insert(v.begin(), X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace(v.begin(), X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + //With emulated move semantics an extra copy is unavoidable + #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST_EQ(insert_results == emplace_results, true); + #endif + { + boost::container::vector 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 = X::special(); + v.insert(v.begin(), x); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace(v.begin(), x); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + BOOST_TEST_EQ(insert_results == emplace_results, true); + { + boost::container::vector 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 = X::special(); + v.insert(v.begin(), boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace(v.begin(), boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + BOOST_TEST_EQ(insert_results == emplace_results, true); + { + boost::container::vector 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 = X::special(); + v.insert(v.begin(), X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace(v.begin(), X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + //With emulated move semantics an extra copy is unavoidable + #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST_EQ(insert_results == emplace_results, true); + #endif + { + boost::container::vector 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 = X::special(); + v.push_back(x); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace_back(x); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + BOOST_TEST_EQ(insert_results == emplace_results, true); + { + boost::container::vector 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 = X::special(); + v.push_back(boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace_back(boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + BOOST_TEST_EQ(insert_results == emplace_results, true); + { + boost::container::vector 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 = X::special(); + v.push_back(X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace_back(X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + //With emulated move semantics an extra copy is unavoidable + #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST_EQ(insert_results == emplace_results, true); + #endif + { + boost::container::vector 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 = X::special(); + v.push_back(x); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace_back(x); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + BOOST_TEST_EQ(insert_results == emplace_results, true); + { + boost::container::vector 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 = X::special(); + v.push_back(boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace_back(boost::move(x)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + BOOST_TEST_EQ(insert_results == emplace_results, true); + { + boost::container::vector 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 = X::special(); + v.push_back(X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + insert_results = X::sp; + } + { + boost::container::vector 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 = X::special(); + v.emplace_back(X(0,0)); + std::cout << X::sp; + std::cout << "----\n"; + emplace_results = X::sp; + } + //With emulated move semantics an extra copy is unavoidable + #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST_EQ(insert_results == emplace_results, true); + #endif + return boost::report_errors(); +} diff --git a/include/boost/container/detail/advanced_insert_int.hpp b/include/boost/container/detail/advanced_insert_int.hpp index b9eb074..4d00d7a 100644 --- a/include/boost/container/detail/advanced_insert_int.hpp +++ b/include/boost/container/detail/advanced_insert_int.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include //std::iterator_traits #include #include @@ -269,6 +270,44 @@ struct insert_emplace_proxy } }; +//Specializations to avoid an unneeded temporary when emplacing from a single argument o type value_type +template +struct insert_emplace_proxy::value_type> + : public insert_move_proxy +{ + explicit insert_emplace_proxy(typename boost::container::allocator_traits::value_type &&v) + : insert_move_proxy(v) + {} +}; + +template +struct insert_emplace_proxy::value_type &> + : public insert_copy_proxy +{ + explicit insert_emplace_proxy(const typename boost::container::allocator_traits::value_type &v) + : insert_copy_proxy(v) + {} +}; + +template +struct insert_emplace_proxy::value_type &> + : public insert_copy_proxy +{ + explicit insert_emplace_proxy(const typename boost::container::allocator_traits::value_type &v) + : insert_copy_proxy(v) + {} +}; + +template +struct insert_emplace_proxy::value_type> + : public insert_copy_proxy +{ + explicit insert_emplace_proxy(const typename boost::container::allocator_traits::value_type &v) + : insert_copy_proxy(v) + {} +}; + + }}} //namespace boost { namespace container { namespace container_detail { #else //#ifdef BOOST_CONTAINER_PERFECT_FORWARDING @@ -347,6 +386,34 @@ struct BOOST_PP_CAT(insert_emplace_proxy_arg, N) #define BOOST_PP_LOCAL_LIMITS (0, BOOST_CONTAINER_MAX_CONSTRUCTOR_PARAMETERS) #include BOOST_PP_LOCAL_ITERATE() +//Specializations to avoid an unneeded temporary when emplacing from a single argument o type value_type +template +struct insert_emplace_proxy_arg1::value_type> > + : public insert_move_proxy +{ + explicit insert_emplace_proxy_arg1(typename boost::container::allocator_traits::value_type &v) + : insert_move_proxy(v) + {} +}; + +template +struct insert_emplace_proxy_arg1::value_type> + : public insert_copy_proxy +{ + explicit insert_emplace_proxy_arg1(const typename boost::container::allocator_traits::value_type &v) + : insert_copy_proxy(v) + {} +}; +/* +template +struct insert_emplace_proxy_arg1::value_type> + : public insert_copy_proxy +{ + explicit insert_emplace_proxy_arg1(const typename boost::container::allocator_traits::value_type &v) + : insert_copy_proxy(v) + {} +}; +*/ }}} //namespace boost { namespace container { namespace container_detail { #endif //#ifdef BOOST_CONTAINER_PERFECT_FORWARDING diff --git a/proj/vc7ide/bench_insert_vs_emplace.vcproj b/proj/vc7ide/bench_insert_vs_emplace.vcproj new file mode 100644 index 0000000..c1469ed --- /dev/null +++ b/proj/vc7ide/bench_insert_vs_emplace.vcproj @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +