diff --git a/README.md b/README.md index c1a02da..cd8130c 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,5 @@ For a full writeup explaining the design and benefits of the Parallel Hashmap, please click [here](https://greg7mdp.github.io/parallel-hashmap/). -> **IMPORTANT:** This repository borrowed a lot of code from the [abseil-cpp](https://github.com/abseil/abseil-cpp) repository, notably for the implementations of `phmap::flat_hash_map`, `phmap::flat_hash_set`, `phmap::node_hash_map` and `phmap::node_hash_set`. Please be aware that the code from [abseil-cpp](https://github.com/abseil/abseil-cpp) has been modified, and may behave differently that the original. +> **IMPORTANT:** This repository borrowed a lot of code from the [abseil-cpp](https://github.com/abseil/abseil-cpp) repository, notably for the implementations of `phmap::flat_hash_map`, `phmap::flat_hash_set`, `phmap::node_hash_map` and `phmap::node_hash_set`. Please be aware that the code from [abseil-cpp](https://github.com/abseil/abseil-cpp) has been modified, and may behave differently than the original. This repository should be construed as an independent work, with no guarantees of any kind implied or provided by the authors. diff --git a/parallel_hashmap/phmap.h b/parallel_hashmap/phmap.h index 2683831..1e8de79 100644 --- a/parallel_hashmap/phmap.h +++ b/parallel_hashmap/phmap.h @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include #include "phmap_bits.h" @@ -2199,13 +2201,13 @@ protected: public: explicit MutexLock_(Mutex *mu) PHMAP_EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { if (this->mu_) - this->mu_->Lock(); + this->mu_->lock(); } void set_mutex(Mutex *mu) PHMAP_NO_THREAD_SAFETY_ANALYSIS { assert(mu && this->mu_ == nullptr); this->mu_ = mu; - this->mu_->Lock(); + this->mu_->lock(); } MutexLock_(const MutexLock_ &) = delete; // NOLINT(runtime/mutex) @@ -2213,10 +2215,10 @@ protected: MutexLock_& operator=(const MutexLock_&) = delete; MutexLock_& operator=(MutexLock_&&) = delete; - ~MutexLock_() PHMAP_UNLOCK_FUNCTION() { if (this->mu_) this->mu_->Unlock(); } + ~MutexLock_() PHMAP_UNLOCK_FUNCTION() { if (this->mu_) this->mu_->unlock(); } private: - Mutex * mu_; + Mutex *mu_; }; // -------------------------------------------------------------------- @@ -3953,6 +3955,98 @@ public: static const Value& value(const value_type* elem) { return elem->second; } }; + +// -------------------------------------------------------------------------- +// hash_default +// -------------------------------------------------------------------------- + +// The hash of an object of type T is computed by using phmap::Hash. +template +struct HashEq +{ + //using Hash = phmap::Hash; + using Hash = std::hash; + using Eq = std::equal_to; +}; + +#if 0 + +struct StringHash +{ + using is_transparent = void; + + size_t operator()(phmap::string_view v) const { + return phmap::Hash{}(v); + } +}; + +// Supports heterogeneous lookup for string-like elements. +struct StringHashEq +{ + using Hash = StringHash; + struct Eq { + using is_transparent = void; + bool operator()(phmap::string_view lhs, phmap::string_view rhs) const { + return lhs == rhs; + } + }; +}; + +template <> +struct HashEq : StringHashEq {}; + +template <> +struct HashEq : StringHashEq {}; + +#endif + +// Supports heterogeneous lookup for pointers and smart pointers. +template +struct HashEq +{ + struct Hash { + using is_transparent = void; + template + size_t operator()(const U& ptr) const { + //return phmap::Hash{}(HashEq::ToPtr(ptr)); + return std::hash{}(HashEq::ToPtr(ptr)); + } + }; + + struct Eq { + using is_transparent = void; + template + bool operator()(const A& a, const B& b) const { + return HashEq::ToPtr(a) == HashEq::ToPtr(b); + } + }; + +private: + static const T* ToPtr(const T* ptr) { return ptr; } + template + static const T* ToPtr(const std::unique_ptr& ptr) { + return ptr.get(); + } + + template + static const T* ToPtr(const std::shared_ptr& ptr) { + return ptr.get(); + } +}; + +template +struct HashEq> : HashEq {}; + +template +struct HashEq> : HashEq {}; + +template +using hash_default_hash = typename container_internal::HashEq::Hash; + +template +using hash_default_eq = typename container_internal::HashEq::Eq; + + namespace debug { // -------------------------------------------------------------------------- @@ -4019,6 +4113,23 @@ struct HashtableDebugAccess> } // namespace debug } // namespace container_internal +// ----------------------------------------------------------------------------- +// NullMutex +// ----------------------------------------------------------------------------- +// 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). +// ----------------------------------------------------------------------------- +class PHMAP_LOCKABLE NullMutex { +public: + NullMutex() {} + ~NullMutex() {} + void lock() PHMAP_EXCLUSIVE_LOCK_FUNCTION() {} + void unlock() PHMAP_UNLOCK_FUNCTION() {} + bool try_lock() PHMAP_EXCLUSIVE_TRYLOCK_FUNCTION(true) { return true; } +}; + + // ----------------------------------------------------------------------------- // phmap::flat_hash_set // ----------------------------------------------------------------------------- diff --git a/parallel_hashmap/phmap_bits.h b/parallel_hashmap/phmap_bits.h index d83fd16..c8e0d7a 100644 --- a/parallel_hashmap/phmap_bits.h +++ b/parallel_hashmap/phmap_bits.h @@ -287,6 +287,166 @@ inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } #endif +// ----------------------------------------------------------------------------- +// File: bits.h +// ----------------------------------------------------------------------------- + +#if defined(_MSC_VER) + // We can achieve something similar to attribute((always_inline)) with MSVC by + // using the __forceinline keyword, however this is not perfect. MSVC is + // much less aggressive about inlining, and even with the __forceinline keyword. + #define PHMAP_BASE_INTERNAL_FORCEINLINE __forceinline +#else + // Use default attribute inline. + #define PHMAP_BASE_INTERNAL_FORCEINLINE inline PHMAP_ATTRIBUTE_ALWAYS_INLINE +#endif + + +namespace phmap { +namespace base_internal { + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { + int zeroes = 60; + if (n >> 32) zeroes -= 32, n >>= 32; + if (n >> 16) zeroes -= 16, n >>= 16; + if (n >> 8) zeroes -= 8, n >>= 8; + if (n >> 4) zeroes -= 4, n >>= 4; + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { +#if defined(_MSC_VER) && defined(_M_X64) + // MSVC does not have __buitin_clzll. Use _BitScanReverse64. + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse64(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(_MSC_VER) + // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse + unsigned long result = 0; // NOLINT(runtime/int) + if ((n >> 32) && _BitScanReverse(&result, n >> 32)) { + return 31 - result; + } + if (_BitScanReverse(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(__GNUC__) + // Use __builtin_clzll, which uses the following instructions: + // x86: bsr + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) + "__builtin_clzll does not take 64-bit arg"); + + // Handle 0 as a special case because __builtin_clzll(0) is undefined. + if (n == 0) { + return 64; + } + return __builtin_clzll(n); +#else + return CountLeadingZeros64Slow(n); +#endif +} + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { + int zeroes = 28; + if (n >> 16) zeroes -= 16, n >>= 16; + if (n >> 8) zeroes -= 8, n >>= 8; + if (n >> 4) zeroes -= 4, n >>= 4; + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { +#if defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse(&result, n)) { + return 31 - result; + } + return 32; +#elif defined(__GNUC__) + // Use __builtin_clz, which uses the following instructions: + // x86: bsr + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(int) == sizeof(n), + "__builtin_clz does not take 32-bit arg"); + + // Handle 0 as a special case because __builtin_clz(0) is undefined. + if (n == 0) { + return 32; + } + return __builtin_clz(n); +#else + return CountLeadingZeros32Slow(n); +#endif +} + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) { + int c = 63; + n &= ~n + 1; + if (n & 0x00000000FFFFFFFF) c -= 32; + if (n & 0x0000FFFF0000FFFF) c -= 16; + if (n & 0x00FF00FF00FF00FF) c -= 8; + if (n & 0x0F0F0F0F0F0F0F0F) c -= 4; + if (n & 0x3333333333333333) c -= 2; + if (n & 0x5555555555555555) c -= 1; + return c; +} + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { +#if defined(_MSC_VER) && defined(_M_X64) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward64(&result, n); + return result; +#elif defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + if (static_cast(n) == 0) { + _BitScanForward(&result, n >> 32); + return result + 32; + } + _BitScanForward(&result, n); + return result; +#elif defined(__GNUC__) + static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) + "__builtin_ctzll does not take 64-bit arg"); + return __builtin_ctzll(n); +#else + return CountTrailingZerosNonZero64Slow(n); +#endif +} + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) { + int c = 31; + n &= ~n + 1; + if (n & 0x0000FFFF) c -= 16; + if (n & 0x00FF00FF) c -= 8; + if (n & 0x0F0F0F0F) c -= 4; + if (n & 0x33333333) c -= 2; + if (n & 0x55555555) c -= 1; + return c; +} + +PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { +#if defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward(&result, n); + return result; +#elif defined(__GNUC__) + static_assert(sizeof(int) == sizeof(n), + "__builtin_ctz does not take 32-bit arg"); + return __builtin_ctz(n); +#else + return CountTrailingZerosNonZero32Slow(n); +#endif +} + +#undef PHMAP_BASE_INTERNAL_FORCEINLINE + +} // namespace base_internal +} // namespace phmap + // ----------------------------------------------------------------------------- // File: optimization.h // -----------------------------------------------------------------------------