// Copyright 2021 Peter Dimov. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING #include #include #include #include #include #ifdef HAVE_ABSEIL # include "absl/container/node_hash_map.h" # include "absl/container/flat_hash_map.h" #endif #include #include #include #include #include using namespace std::chrono_literals; static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint64_t s, std::size_t size ) { auto t2 = std::chrono::steady_clock::now(); std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n"; t1 = t2; } constexpr unsigned N = 2'000'000; constexpr int K = 10; static std::vector< std::uint64_t > indices1, indices2, indices3; static void init_indices() { indices1.push_back( 0 ); for( unsigned i = 1; i <= N*2; ++i ) { indices1.push_back( i ); } indices2.push_back( 0 ); { boost::detail::splitmix64 rng; for( unsigned i = 1; i <= N*2; ++i ) { indices2.push_back( rng() ); } } indices3.push_back( 0 ); for( unsigned i = 1; i <= N*2; ++i ) { indices3.push_back( (std::uint64_t)i << 40 ); } } template void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 ) { for( unsigned i = 1; i <= N; ++i ) { map.insert( { indices1[ i ], i } ); } print_time( t1, "Consecutive insert", 0, map.size() ); for( unsigned i = 1; i <= N; ++i ) { map.insert( { indices2[ i ], i } ); } print_time( t1, "Random insert", 0, map.size() ); for( unsigned i = 1; i <= N; ++i ) { map.insert( { indices3[ i ], i } ); } print_time( t1, "Consecutive shifted insert", 0, map.size() ); std::cout << std::endl; } template void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 ) { std::uint64_t s; s = 0; for( int j = 0; j < K; ++j ) { for( unsigned i = 1; i <= N * 2; ++i ) { auto it = map.find( indices1[ i ] ); if( it != map.end() ) s += it->second; } } print_time( t1, "Consecutive lookup", s, map.size() ); s = 0; for( int j = 0; j < K; ++j ) { for( unsigned i = 1; i <= N * 2; ++i ) { auto it = map.find( indices2[ i ] ); if( it != map.end() ) s += it->second; } } print_time( t1, "Random lookup", s, map.size() ); s = 0; for( int j = 0; j < K; ++j ) { for( unsigned i = 1; i <= N * 2; ++i ) { auto it = map.find( indices3[ i ] ); if( it != map.end() ) s += it->second; } } print_time( t1, "Consecutive shifted lookup", s, map.size() ); std::cout << std::endl; } template void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 ) { auto it = map.begin(); while( it != map.end() ) { if( it->second & 1 ) { map.erase( it++ ); } else { ++it; } } print_time( t1, "Iterate and erase odd elements", 0, map.size() ); std::cout << std::endl; } template void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 ) { for( unsigned i = 1; i <= N; ++i ) { map.erase( indices1[ i ] ); } print_time( t1, "Consecutive erase", 0, map.size() ); { boost::detail::splitmix64 rng; for( unsigned i = 1; i <= N; ++i ) { map.erase( indices2[ i ] ); } } print_time( t1, "Random erase", 0, map.size() ); for( unsigned i = 1; i <= N; ++i ) { map.erase( indices3[ i ] ); } print_time( t1, "Consecutive shifted erase", 0, map.size() ); std::cout << std::endl; } static std::vector< std::pair > times; template class Map> void test( char const* label ) { std::cout << label << ":\n\n"; Map map; auto t0 = std::chrono::steady_clock::now(); auto t1 = t0; test_insert( map, t1 ); test_lookup( map, t1 ); test_iteration( map, t1 ); test_lookup( map, t1 ); test_erase( map, t1 ); auto tN = std::chrono::steady_clock::now(); std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n"; times.push_back( { label, ( tN - t0 ) / 1ms } ); } // multi_index emulation of unordered_map template struct pair { K first; mutable V second; }; using namespace boost::multi_index; template using multi_index_map = multi_index_container< pair, indexed_by< hashed_unique< member, K, &pair::first> > > >; int main() { init_indices(); test( "std::unordered_map" ); test( "boost::unordered_map" ); test( "multi_index_map" ); // test( "std::map" ); #ifdef HAVE_ABSEIL test( "absl::node_hash_map" ); test( "absl::flat_hash_map" ); #endif std::cout << "---\n\n"; for( auto const& x: times ) { std::cout << x.first << ": " << x.second << " ms\n"; } } #ifdef HAVE_ABSEIL # include "absl/container/internal/raw_hash_set.cc" # include "absl/hash/internal/hash.cc" # include "absl/hash/internal/low_level_hash.cc" # include "absl/hash/internal/city.cc" #endif