/* Measuring performance of concurrent hashmaps under several * workload configurations. * * Copyright 2023 Joaquin M Lopez Munoz. * 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) */ #include #include #include #include std::chrono::high_resolution_clock::time_point measure_start,measure_pause; template double measure(F f) { using namespace std::chrono; static const int num_trials=10; static const milliseconds min_time_per_trial(10); std::array trials; for(int i=0;i>(t2-measure_start).count()/runs; } std::sort(trials.begin(),trials.end()); return std::accumulate( trials.begin()+2,trials.end()-2,0.0)/(trials.size()-4); } void pause_timing() { measure_pause=std::chrono::high_resolution_clock::now(); } void resume_timing() { measure_start+=std::chrono::high_resolution_clock::now()-measure_pause; } #include #include #include #include #include #include #include #include "gtl/phmap.hpp" #include "oneapi/tbb/concurrent_hash_map.h" #include "zipfian_int_distribution.h" using boost_map=boost::concurrent_flat_map; using tbb_map=tbb::concurrent_hash_map; using gtl_map=gtl::parallel_flat_hash_map< int,int,gtl::priv::hash_default_hash,gtl::priv::hash_default_eq, std::allocator>, 8,std::mutex>; template inline void map_update(boost_map& m,Args&&... args) { m.emplace_or_visit(std::forward(args)...,[](auto& x){++x.second;}); } template inline bool map_find(const boost_map& m,const Key& x) { return m.contains(x); } template inline void map_update(tbb_map& m,Args&&... args) { tbb_map::accessor acc; if(!m.emplace(acc,std::forward(args)...))++acc->second; } template inline bool map_find(const tbb_map& m,const Key& x) { return m.count(x); } template inline void map_update(gtl_map& m,Key&& k,Args&&... args) { m.lazy_emplace_l( k, [](auto& x){++x.second;}, [&](const auto& ctor){ ctor(std::forward(k),std::forward(args)...);}); } template inline bool map_find(const gtl_map& m,const Key& x) { return m.contains(x); } template class updater { public: updater(const Distribution& dist_):dist{dist_}{} template void operator()(Map& m,URNG& gen) { map_update(m,dist(gen),0); } private: Distribution dist; }; template class finder { public: finder(const Distribution& dist_):dist{dist_}{} template void operator()(const Map& m,URNG& gen) { if(map_find(m,dist(gen)))++res; } int res=0; private: Distribution dist; }; template struct parallel_load { using result_type=std::size_t; BOOST_NOINLINE result_type operator()(int N,double theta,int num_threads)const { int res=0; pause_timing(); { Map m; std::vector threads; std::vector results(num_threads); zipfian_int_distribution zipf1{1,N,theta}, zipf2{N+1,2*N,theta}; std::latch ready(num_threads), start(1), completed(num_threads), die(1); if constexpr(std::is_same_v)m.rehash(N); for(int i=0;i dist({10,45,45}); std::mt19937_64 gen(std::size_t(282472+i*213731)); updater update{zipf1}; finder successful_find{zipf1}, unsuccessful_find{zipf2}; ready.arrive_and_wait(); start.wait(); int n=i==0?(N+num_threads-1)/num_threads:N/num_threads; for(int j=0;j class Tester, typename Container1,typename Container2,typename Container3 > BOOST_NOINLINE void test( const char* title,int N,double theta, const char* name1,const char* name2,const char* name3) { #ifdef NUM_THREADS const int num_threads=NUM_THREADS; #else const int num_threads=16; #endif std::cout<(),N,theta,n)); std::cout<(),N,theta,n)); std::cout<(),N,theta,n)); std::cout< ( "Parallel load",N,theta, "tbb::concurrent_hash_map", "gtl::parallel_flat_hash_map", "boost::concurrent_flat_map" ); } } }