diff --git a/benchmark/Makefile b/benchmark/Makefile index 2a56718..608bd59 100644 --- a/benchmark/Makefile +++ b/benchmark/Makefile @@ -8,6 +8,7 @@ PROGS = stl_unordered_map sparsepp phmap abseil_flat abseil_parallel_flat BUILD_PROGS = $(addprefix build/,$(PROGS)) SIZE = 100000000 ABSEIL = ../../abseil-cpp +PHMAP_SRC = ../parallel_hashmap all: test @@ -20,7 +21,7 @@ build/stl_unordered_map: bench.cc Makefile build/sparsepp: bench.cc Makefile $(CXX) -DSPARSEPP -I../../sparsepp bench.cc -o $@ -build/phmap: bench.cc Makefile +build/phmap: bench.cc Makefile $(PHMAP_SRC)/phmap.h $(CXX) -DPHMAP -I.. bench.cc /MD -o $@ build/abseil_flat: bench.cc Makefile @@ -34,7 +35,8 @@ progs: $(BUILD_PROGS) test: builddir progs -rm -f output #./build/stl_unordered_map $(SIZE) random >> output - ./build/abseil_flat $(SIZE) random >> output + #./build/abseil_flat $(SIZE) random >> output + ./build/phmap $(SIZE) random >> output #./build/sparsepp $(SIZE) random >> output ./build/abseil_parallel_flat $(SIZE) random >> output python make_chart_data.py < output @@ -47,11 +49,8 @@ test_cust: ./build/abseil_parallel_flat $(SIZE) random >> output python make_chart_data.py < output - - chart: python make_chart_data.py < output clean: -rm -fr output build - diff --git a/benchmark/bench.cc b/benchmark/bench.cc index 7aa624b..4ce2898 100644 --- a/benchmark/bench.cc +++ b/benchmark/bench.cc @@ -17,27 +17,29 @@ #if defined(ABSEIL_PARALLEL_FLAT) #include "absl/container/parallel_flat_hash_map.h" #define MAPNAME absl::parallel_flat_hash_map + #define NMSP absl #else #include "parallel_hashmap/phmap.h" #define MAPNAME phmap::parallel_flat_hash_map + #define NMSP phmap #endif - //#define MT_SUPPORT 2 + #define MT_SUPPORT 1 #if MT_SUPPORT == 1 // create the parallel_flat_hash_map without internal mutexes, for when // we programatically ensure that each thread uses different internal submaps // -------------------------------------------------------------------------- - #define EXTRAARGS , absl::container_internal::hash_default_hash, \ - absl::container_internal::hash_default_eq, \ - std::allocator>, 4, absl::NullMutex + #define EXTRAARGS , NMSP::container_internal::hash_default_hash, \ + NMSP::container_internal::hash_default_eq, \ + std::allocator>, 4, NMSP::NullMutex #elif MT_SUPPORT == 2 // create the parallel_flat_hash_map with internal mutexes, for when // we read/write the same parallel_flat_hash_map from multiple threads, // without any special precautions. // -------------------------------------------------------------------------- - #define EXTRAARGS , absl::container_internal::hash_default_hash, \ - absl::container_internal::hash_default_eq, \ - std::allocator>, 4, absl::Mutex + #define EXTRAARGS , NMSP::container_internal::hash_default_hash, \ + NMSP::container_internal::hash_default_eq, \ + std::allocator>, 4, NMSP::Mutex #else #define EXTRAARGS #endif @@ -357,8 +359,11 @@ int main(int argc, char ** argv) srand(1); // for a fair/deterministic comparison Timer timer(true); - if (!strcmp(program_slug,"absl::parallel_flat_hash_map")) +#ifdef MT_SUPPORT + if (!strcmp(program_slug,"absl::parallel_flat_hash_map") || + !strcmp(program_slug,"phmap::parallel_flat_hash_map")) program_slug = xstr(MAPNAME) "_mt"; +#endif std::thread t1(memlog); diff --git a/benchmark/make_chart_data.py b/benchmark/make_chart_data.py index 41714c4..573c9f8 100644 --- a/benchmark/make_chart_data.py +++ b/benchmark/make_chart_data.py @@ -44,6 +44,8 @@ proper_names = { 'std::unordered_map': 'std::unordered_map (1 thread)', 'spp::sparse_hash_map': 'sparsepp (1 thread, use_spp_alloc)', 'absl::flat_hash_map': 'absl::flat_hash_map (1 thread)', + 'phmap::parallel_flat_hash_map': 'phmap::parallel_flat_hash_map (1 thread)', + 'phmap::parallel_flat_hash_map_mt': 'phmap::parallel_flat_hash_map (8 thread8)', 'absl::parallel_flat_hash_map': 'absl::parallel_flat_hash_map (1 thread)', 'absl::parallel_flat_hash_map_mt': 'absl::parallel_flat_hash_map (8 threads)', 'absl::parallel_flat_hash_map_4': 'absl::parallel_flat_hash_map (N=4, 8 threads)', @@ -52,14 +54,16 @@ proper_names = { } proper_color = { - 'std::unordered_map': 0, + 'std::unordered_map': 0, 'spp::sparse_hash_map': 0, - 'absl::flat_hash_map': 1, - 'absl::parallel_flat_hash_map': 2, - 'absl::parallel_flat_hash_map_mt': 2, - 'absl::parallel_flat_hash_map_4': 2, - 'absl::parallel_flat_hash_map_5': 3, - 'absl::parallel_flat_hash_map_6': 4 + 'absl::flat_hash_map': 1, + 'phmap::parallel_flat_hash_map': 2, + 'phmap::parallel_flat_hash_map_mt': 2, + 'absl::parallel_flat_hash_map': 3, + 'absl::parallel_flat_hash_map_mt': 3, + 'absl::parallel_flat_hash_map_4': 3, + 'absl::parallel_flat_hash_map_5': 4, + 'absl::parallel_flat_hash_map_6': 5 } bench_titles = { @@ -79,6 +83,8 @@ program_slugs = [ 'sparsepp', 'absl::flat_hash_map', 'absl::parallel_flat_hash_map', + 'phmap::parallel_flat_hash_map', + 'phmap::parallel_flat_hash_map_mt', 'absl::parallel_flat_hash_map_mt', 'absl::parallel_flat_hash_map_4', 'absl::parallel_flat_hash_map_5', diff --git a/parallel_hashmap/phmap.h b/parallel_hashmap/phmap.h index 1e8de79..13c5c06 100644 --- a/parallel_hashmap/phmap.h +++ b/parallel_hashmap/phmap.h @@ -34,8 +34,6 @@ // limitations under the License. // --------------------------------------------------------------------------- - - #include #include #include @@ -263,6 +261,7 @@ inline size_t HashSeed(const ctrl_t* ctrl) { inline size_t H1(size_t hash, const ctrl_t* ctrl) { return (hash >> 7) ^ HashSeed(ctrl); } + inline ctrl_t H2(size_t hash) { return hash & 0x7F; } inline bool IsEmpty(ctrl_t c) { return c == kEmpty; } @@ -301,6 +300,7 @@ struct GroupSse2Impl } // Returns a bitmask representing the positions of slots that match hash. + // ---------------------------------------------------------------------- BitMask Match(h2_t hash) const { auto match = _mm_set1_epi8(hash); return BitMask( @@ -308,6 +308,7 @@ struct GroupSse2Impl } // Returns a bitmask representing the positions of empty slots. + // ------------------------------------------------------------ BitMask MatchEmpty() const { #if PHMAP_HAVE_SSSE3 // This only works because kEmpty is -128. @@ -319,6 +320,7 @@ struct GroupSse2Impl } // Returns a bitmask representing the positions of empty or deleted slots. + // ----------------------------------------------------------------------- BitMask MatchEmptyOrDeleted() const { auto special = _mm_set1_epi8(kSentinel); return BitMask( @@ -326,12 +328,14 @@ struct GroupSse2Impl } // Returns the number of trailing empty or deleted elements in the group. + // ---------------------------------------------------------------------- uint32_t CountLeadingEmptyOrDeleted() const { auto special = _mm_set1_epi8(kSentinel); return TrailingZeros( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1); } + // ---------------------------------------------------------------------- void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { auto msbs = _mm_set1_epi8(static_cast(-128)); auto x126 = _mm_set1_epi8(126); @@ -404,7 +408,7 @@ struct GroupPortableImpl uint64_t ctrl; }; -#if PHMAP_HAVE_SSE2 +#if PHMAP_HAVE_SSE2 using Group = GroupSse2Impl; #else using Group = GroupPortableImpl; @@ -3971,6 +3975,20 @@ struct HashEq #if 0 +struct int64_t_hash +{ + using is_transparent = void; + size_t operator()(int64_t v) const { + return (size_t)v; + } +}; + +template <> +struct HashEq { + using Hash = int64_t_hash; + using Eq = std::equal_to; +}; + struct StringHash { using is_transparent = void; @@ -4118,7 +4136,7 @@ struct HashtableDebugAccess> // ----------------------------------------------------------------------------- // A class that implements the Mutex interface, but does nothing. This is to be // used as a default template parameters for classes who provide optional -// internal locking (like absl::parallel_flat_hash_map). +// internal locking (like phmap::parallel_flat_hash_map). // ----------------------------------------------------------------------------- class PHMAP_LOCKABLE NullMutex { public: