Update foa to construct stack-locals with the user's Allocator during emplace()

This commit is contained in:
Christian Mazakas 2023-02-23 11:06:14 -08:00
parent 0ff1fa0f6e
commit 86318c1e88
5 changed files with 126 additions and 69 deletions

View File

@ -1129,6 +1129,25 @@ _STL_RESTORE_DEPRECATED_WARNING
*/ */
constexpr static float const mlf = 0.875f; constexpr static float const mlf = 0.875f;
template <class T>
union storage
{
T t_;
storage(){}
~storage(){}
};
template <class TypePolicy,class A,class T>
struct drop_guard
{
using type_policy=TypePolicy;
A& a;
T* p;
~drop_guard(){type_policy::destroy(a,p);};
};
/* foa::table interface departs in a number of ways from that of C++ unordered /* foa::table interface departs in a number of ways from that of C++ unordered
* associative containers because it's not for end-user consumption * associative containers because it's not for end-user consumption
* (boost::unordered_[flat|node]_[map|set]) wrappers complete it as * (boost::unordered_[flat|node]_[map|set]) wrappers complete it as
@ -1418,18 +1437,35 @@ public:
template<typename... Args> template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args) BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
{ {
/* We dispatch based on whether or not the value_type is constructible from using emplace_type=typename std::conditional<
* an rvalue reference of the deduced emplace_type. We do this specifically std::is_constructible<init_type,Args...>::value,
* for the case of the node-based containers. To this end, we're able to init_type,
* avoid allocating a node when a duplicate element is attempted to be value_type
* inserted. For immovable types, we instead dispatch to the routine that >::type;
* unconditionally allocates via `type_policy::construct()`.
*/ using insert_type=typename std::conditional<
return emplace_value(
std::is_constructible< std::is_constructible<
value_type, value_type,emplace_type>::value,
emplace_type<Args...>&&>{}, emplace_type,element_type
std::forward<Args>(args)...); >::type;
using alloc_insert_type=typename std::conditional<
std::is_constructible<
value_type,emplace_type>::value,
emplace_type,value_type
>::type;
using alloc_type=
typename boost::allocator_rebind<Allocator,alloc_insert_type>::type;
storage<insert_type> s;
alloc_type alloc{al()};
auto *p=std::addressof(s.t_);
type_policy::construct(alloc,p,std::forward<Args>(args)...);
drop_guard<type_policy,alloc_type,insert_type> guard{alloc,p};
return emplace_impl(type_policy::move(*p));
} }
template<typename Key,typename... Args> template<typename Key,typename... Args>
@ -1614,15 +1650,6 @@ private:
template<typename,typename,typename,typename> friend class table; template<typename,typename,typename,typename> friend class table;
using arrays_type=table_arrays<element_type,group_type,size_policy>; using arrays_type=table_arrays<element_type,group_type,size_policy>;
template<typename... Args>
using emplace_type = typename std::conditional<
std::is_constructible<
init_type,Args...
>::value,
init_type,
value_type
>::type;
struct clear_on_exit struct clear_on_exit
{ {
~clear_on_exit(){x.clear();} ~clear_on_exit(){x.clear();}
@ -1904,29 +1931,6 @@ private:
#pragma warning(pop) /* C4800 */ #pragma warning(pop) /* C4800 */
#endif #endif
template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_value(
std::true_type /* movable value_type */,Args&&... args
) {
using emplace_type_t = emplace_type<Args...>;
return emplace_impl(emplace_type_t(std::forward<Args>(args)...));
}
template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_value(
std::false_type /* immovable value_type */,Args&&... args
) {
alignas(element_type)
unsigned char buf[sizeof(element_type)];
element_type* p = reinterpret_cast<element_type*>(buf);
type_policy::construct(al(),p,std::forward<Args>(args)...);
destroy_element_on_exit d{this,p};
(void)d;
return emplace_impl(type_policy::move(*p));
}
template<typename... Args> template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args) BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
{ {

View File

@ -105,6 +105,9 @@ struct node_handle_base
node_handle_base& operator=(node_handle_base&& nh)noexcept node_handle_base& operator=(node_handle_base&& nh)noexcept
{ {
element_type x;
x.p=p_;
if(this!=&nh){ if(this!=&nh){
if(empty()){ if(empty()){
if(nh.empty()){ /* empty(), nh.empty() */ if(nh.empty()){ /* empty(), nh.empty() */
@ -115,7 +118,7 @@ struct node_handle_base
} }
}else{ }else{
if(nh.empty()){ /* !empty(), nh.empty() */ if(nh.empty()){ /* !empty(), nh.empty() */
type_policy::destroy(al(),p_); type_policy::destroy(al(),&x);
reset(); reset();
}else{ /* !empty(), !nh.empty() */ }else{ /* !empty(), !nh.empty() */
bool const pocma= bool const pocma=
@ -124,7 +127,7 @@ struct node_handle_base
BOOST_ASSERT(pocma||al()==nh.al()); BOOST_ASSERT(pocma||al()==nh.al());
type_policy::destroy(al(),p_); type_policy::destroy(al(),&x);
if(pocma){ if(pocma){
al()=std::move(nh.al()); al()=std::move(nh.al());
} }
@ -137,7 +140,7 @@ struct node_handle_base
if(empty()){ /* empty(), nh.empty() */ if(empty()){ /* empty(), nh.empty() */
/* nothing to do */ /* nothing to do */
}else{ /* !empty(), !nh.empty() */ }else{ /* !empty(), !nh.empty() */
type_policy::destroy(al(),p_); type_policy::destroy(al(),&x);
reset(); reset();
} }
} }
@ -147,7 +150,9 @@ struct node_handle_base
~node_handle_base() ~node_handle_base()
{ {
if(!empty()){ if(!empty()){
type_policy::destroy(al(),p_); element_type x;
x.p=p_;
type_policy::destroy(al(),&x);
reset(); reset();
} }
} }

View File

@ -53,6 +53,11 @@ namespace boost {
return kv.first; return kv.first;
} }
static moved_type move(init_type& x)
{
return {std::get<0>(std::move(x)), std::get<1>(std::move(x))};
}
static moved_type move(element_type& x) static moved_type move(element_type& x)
{ {
// TODO: we probably need to launder here // TODO: we probably need to launder here
@ -61,11 +66,26 @@ namespace boost {
} }
template <class A, class... Args> template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args) static void construct(A& al, init_type* p, Args&&... args)
{ {
boost::allocator_construct(al, p, std::forward<Args>(args)...); boost::allocator_construct(al, p, std::forward<Args>(args)...);
} }
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
using alloc_type =
typename boost::allocator_rebind<A, value_type>::type;
alloc_type alloc(al);
std::allocator_traits<alloc_type>::construct(
alloc, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, element_type* p) noexcept template <class A> static void destroy(A& al, element_type* p) noexcept
{ {
boost::allocator_destroy(al, p); boost::allocator_destroy(al, p);

View File

@ -77,6 +77,11 @@ namespace boost {
} }
static element_type&& move(element_type& x) { return std::move(x); } static element_type&& move(element_type& x) { return std::move(x); }
static moved_type move(init_type& x)
{
return {std::get<0>(std::move(x)), std::get<1>(std::move(x))};
}
static moved_type move(value_type& x) static moved_type move(value_type& x)
{ {
return {std::move(const_cast<raw_key_type&>(x.first)), return {std::move(const_cast<raw_key_type&>(x.first)),
@ -96,6 +101,18 @@ namespace boost {
construct(al, p, *copy.p); construct(al, p, *copy.p);
} }
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args> template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args) static void construct(A& al, element_type* p, Args&&... args)
{ {
@ -106,10 +123,11 @@ namespace boost {
} }
BOOST_CATCH(...) BOOST_CATCH(...)
{ {
boost::allocator_deallocate(al, using pointer_type=typename boost::allocator_pointer<A>::type;
boost::pointer_traits< using pointer_traits=boost::pointer_traits<pointer_type>;
typename boost::allocator_pointer<A>::type>::pointer_to(*p->p),
1); boost::allocator_deallocate(
al,pointer_traits::pointer_to(*(p->p)),1);
BOOST_RETHROW BOOST_RETHROW
} }
BOOST_CATCH_END BOOST_CATCH_END
@ -118,16 +136,22 @@ namespace boost {
template <class A> static void destroy(A& al, value_type* p) noexcept template <class A> static void destroy(A& al, value_type* p) noexcept
{ {
boost::allocator_destroy(al, p); boost::allocator_destroy(al, p);
boost::allocator_deallocate(al, }
boost::pointer_traits<
typename boost::allocator_pointer<A>::type>::pointer_to(*p), template <class A> static void destroy(A& al, init_type* p) noexcept
1); {
boost::allocator_destroy(al, p);
} }
template <class A> static void destroy(A& al, element_type* p) noexcept template <class A> static void destroy(A& al, element_type* p) noexcept
{ {
if (p->p) { if (p->p) {
destroy(al,p->p); using pointer_type=typename boost::allocator_pointer<A>::type;
using pointer_traits=boost::pointer_traits<pointer_type>;
destroy(al, p->p);
boost::allocator_deallocate(
al,pointer_traits::pointer_to(*(p->p)), 1);
} }
} }
}; };
@ -137,8 +161,7 @@ namespace boost {
: public detail::foa::node_handle_base<TypePolicy, Allocator> : public detail::foa::node_handle_base<TypePolicy, Allocator>
{ {
private: private:
using base_type = using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy; using typename base_type::type_policy;

View File

@ -78,6 +78,12 @@ namespace boost {
x.p = nullptr; x.p = nullptr;
} }
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args> template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args) static void construct(A& al, element_type* p, Args&&... args)
{ {
@ -100,16 +106,16 @@ namespace boost {
template <class A> static void destroy(A& al, value_type* p) noexcept template <class A> static void destroy(A& al, value_type* p) noexcept
{ {
boost::allocator_destroy(al, p); boost::allocator_destroy(al, p);
boost::allocator_deallocate(al,
boost::pointer_traits<
typename boost::allocator_pointer<A>::type>::pointer_to(*p),
1);
} }
template <class A> static void destroy(A& al, element_type* p) noexcept template <class A> static void destroy(A& al, element_type* p) noexcept
{ {
if (p->p) { if (p->p) {
destroy(al, p->p); destroy(al, p->p);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
} }
} }
}; };
@ -119,8 +125,7 @@ namespace boost {
: public detail::foa::node_handle_base<TypePolicy, Allocator> : public detail::foa::node_handle_base<TypePolicy, Allocator>
{ {
private: private:
using base_type = using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy; using typename base_type::type_policy;
@ -391,7 +396,7 @@ namespace boost {
BOOST_ASSERT(get_allocator() == nh.get_allocator()); BOOST_ASSERT(get_allocator() == nh.get_allocator());
typename set_types::element_type x; typename set_types::element_type x;
x.p=std::addressof(nh.element()); x.p = std::addressof(nh.element());
auto itp = table_.insert(std::move(x)); auto itp = table_.insert(std::move(x));
if (itp.second) { if (itp.second) {
@ -411,7 +416,7 @@ namespace boost {
BOOST_ASSERT(get_allocator() == nh.get_allocator()); BOOST_ASSERT(get_allocator() == nh.get_allocator());
typename set_types::element_type x; typename set_types::element_type x;
x.p=std::addressof(nh.element()); x.p = std::addressof(nh.element());
auto itp = table_.insert(std::move(x)); auto itp = table_.insert(std::move(x));
if (itp.second) { if (itp.second) {
@ -478,7 +483,7 @@ namespace boost {
node_type extract(key_type const& key) node_type extract(key_type const& key)
{ {
auto pos = find(key); auto pos = find(key);
return pos!=end()?extract(pos):node_type(); return pos != end() ? extract(pos) : node_type();
} }
template <class K> template <class K>
@ -489,7 +494,7 @@ namespace boost {
extract(K const& key) extract(K const& key)
{ {
auto pos = find(key); auto pos = find(key);
return pos!=end()?extract(pos):node_type(); return pos != end() ? extract(pos) : node_type();
} }
template <class H2, class P2> template <class H2, class P2>