From 31c33faf57a34e79d983389a4540e5893bb90c32 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 25 Dec 2023 20:25:34 +0100 Subject: [PATCH] added almost-latch-free insertion for regular-layout group_type --- .../unordered/detail/foa/concurrent_table.hpp | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index e93784a6..942a3cce 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -1436,10 +1436,103 @@ private: bool commit_=false; }; + struct latch_free_reserve_slot + { + latch_free_reserve_slot(group_type* pg_,std::size_t pos_):pg{pg_},pos{pos_} + { + unsigned char expected=0; + succeeded_=reinterpret_cast*>(pg)[pos]. + compare_exchange_weak(expected,1); + } + + ~latch_free_reserve_slot() + { + if(succeeded_&&!commit_)pg->reset(pos); + } + + bool succeeded()const{return succeeded_;} + + void commit(){commit_=true;} + + group_type *pg; + std::size_t pos; + bool succeeded_; + bool commit_=false; + }; + + struct assign_insert_counter_on_exit + { + ~assign_insert_counter_on_exit(){counter=x;} + + group_insert_counter_type &counter; + boost::uint32_t x; + }; + template BOOST_FORCEINLINE int unprotected_norehash_emplace_or_visit( GroupAccessMode access_mode,F&& f,Args&&... args) + { + return unprotected_norehash_emplace_or_visit_with_layout( + std::integral_constant{}, + access_mode,std::forward(f),std::forward(args)...); + } + + template + BOOST_FORCEINLINE int + unprotected_norehash_emplace_or_visit_with_layout( + std::true_type, /* regular layout, almost-latch-free */ + GroupAccessMode access_mode,F&& f,Args&&... args) + { + const auto &k=this->key_from(std::forward(args)...); + auto hash=this->hash_for(k); + auto pos0=this->position_for(hash); + + for(;;){ + startover: + boost::uint32_t counter=0; + while(BOOST_UNLIKELY((counter=insert_counter(pos0))%2==1)){} + if(unprotected_visit( + access_mode,k,pos0,hash,std::forward(f)))return 0; + + reserve_size rsize(*this); + if(BOOST_LIKELY(rsize.succeeded())){ + for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){ + auto pos=pb.get(); + auto pg=this->arrays.groups()+pos; + auto mask=pg->match_available(); + if(BOOST_LIKELY(mask!=0)){ + auto n=unchecked_countr_zero(mask); + latch_free_reserve_slot rslot{pg,n}; + if(BOOST_UNLIKELY(!rslot.succeeded())){ + /* slot wasn't empty */ + goto startover; + } + if(BOOST_UNLIKELY( + !insert_counter(pos0).compare_exchange_weak(counter,counter+1))){ + /* other thread inserted from pos0, need to start over */ + goto startover; + } + assign_insert_counter_on_exit a{insert_counter(pos0),counter+2}; + auto p=this->arrays.elements()+pos*N+n; + this->construct_element(p,std::forward(args)...); + pg->set(n,hash); + rslot.commit(); + rsize.commit(); + return 1; + } + pg->mark_overflow(hash); + } + } + else return -1; + } + } + + template + BOOST_FORCEINLINE int + unprotected_norehash_emplace_or_visit_with_layout( + std::false_type, /* non-regular layout, latched */ + GroupAccessMode access_mode,F&& f,Args&&... args) { const auto &k=this->key_from(std::forward(args)...); auto hash=this->hash_for(k);