From 64cb1d59a67c0b50017f07d66195408ef03248f8 Mon Sep 17 00:00:00 2001 From: greg Date: Sun, 17 Mar 2019 14:01:23 -0400 Subject: [PATCH] wip --- parallel_hashmap/phmap.h | 94 ++ parallel_hashmap/phmap_base.h | 2593 ++++++++++++++++++++++++++++----- 2 files changed, 2287 insertions(+), 400 deletions(-) diff --git a/parallel_hashmap/phmap.h b/parallel_hashmap/phmap.h index 4a7e946..399b5e6 100644 --- a/parallel_hashmap/phmap.h +++ b/parallel_hashmap/phmap.h @@ -473,6 +473,100 @@ inline size_t GrowthToLowerboundCapacity(size_t growth) return growth + static_cast((static_cast(growth) - 1) / 7); } +namespace hashtable_debug_internal { + +// If it is a map, call get<0>(). +using std::get; +template +auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) { + return get<0>(pair); +} + +// If it is not a map, return the value directly. +template +const typename T::key_type& GetKey(const typename T::key_type& key, char) { + return key; +} + +// -------------------------------------------------------------------------- +// Containers should specialize this to provide debug information for that +// container. +// -------------------------------------------------------------------------- +template +struct HashtableDebugAccess +{ + // Returns the number of probes required to find `key` in `c`. The "number of + // probes" is a concept that can vary by container. Implementations should + // return 0 when `key` was found in the minimum number of operations and + // should increment the result for each non-trivial operation required to find + // `key`. + // + // The default implementation uses the bucket api from the standard and thus + // works for `std::unordered_*` containers. + // -------------------------------------------------------------------------- + static size_t GetNumProbes(const Container& c, + const typename Container::key_type& key) { + if (!c.bucket_count()) return {}; + size_t num_probes = 0; + size_t bucket = c.bucket(key); + for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) { + if (it == e) return num_probes; + if (c.key_eq()(key, GetKey(*it, 0))) return num_probes; + } + } +}; + +} // namespace hashtable_debug_internal + +// ---------------------------------------------------------------------------- +// I N F O Z S T U B S +// ---------------------------------------------------------------------------- +struct HashtablezInfo +{ + void PrepareForSampling() {} +}; + +inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired) {} + +inline void RecordEraseSlow(HashtablezInfo* info) {} + +HashtablezInfo* SampleSlow(int64_t* next_sample) { return nullptr; } +void UnsampleSlow(HashtablezInfo* info) {} + +class HashtablezInfoHandle +{ +public: + inline void RecordStorageChanged(size_t size, size_t capacity) {} + inline void RecordRehash(size_t total_probe_length) {} + inline void RecordInsert(size_t hash, size_t distance_from_desired) {} + inline void RecordErase() {} + friend inline void swap(HashtablezInfoHandle& lhs, + HashtablezInfoHandle& rhs) {} +}; + +inline HashtablezInfoHandle Sample() { return HashtablezInfoHandle(); } + +class HashtablezSampler +{ +public: + // Returns a global Sampler. + static HashtablezSampler& Global() { static HashtablezSampler hzs; return hzs; } + HashtablezInfo* Register() { static HashtablezInfo info; return &info; } + void Unregister(HashtablezInfo* sample) {} + + using DisposeCallback = void (*)(const HashtablezInfo&); + DisposeCallback SetDisposeCallback(DisposeCallback f) {} + int64_t Iterate(const std::function& f) {} +}; + +void SetHashtablezEnabled(bool enabled) {} +void SetHashtablezSampleParameter(int32_t rate) {} +void SetHashtablezMaxSamples(int32_t max) {} + + // ---------------------------------------------------------------------------- // R A W _ H A S H _ S E T // ---------------------------------------------------------------------------- diff --git a/parallel_hashmap/phmap_base.h b/parallel_hashmap/phmap_base.h index 5c7a419..e728e71 100644 --- a/parallel_hashmap/phmap_base.h +++ b/parallel_hashmap/phmap_base.h @@ -34,9 +34,18 @@ // limitations under the License. // --------------------------------------------------------------------------- -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include + #include "phmap_config.h" @@ -679,408 +688,10 @@ private: } // namespace container_internal } // namespace phmap -// ----------------------------------------------------------------------------- -// optional.h -// ----------------------------------------------------------------------------- -#ifdef PHMAP_HAVE_STD_OPTIONAL - -#include // IWYU pragma: export - -namespace phmap { -using std::bad_optional_access; -using std::optional; -using std::make_optional; -using std::nullopt_t; -using std::nullopt; -} // namespace phmap - -#else - -#if defined(__clang__) - #if __has_feature(cxx_inheriting_constructors) - #define PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 - #endif -#elif (defined(__GNUC__) && \ - (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \ - (__cpp_inheriting_constructors >= 200802) || \ - (defined(_MSC_VER) && _MSC_VER >= 1910) - - #define PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 -#endif - -namespace phmap { - -class bad_optional_access : public std::exception -{ -public: - bad_optional_access() = default; - ~bad_optional_access() override; - const char* what() const noexcept override; -}; - -template -class optional; - -// -------------------------------- -struct nullopt_t -{ - struct init_t {}; - static init_t init; - - explicit constexpr nullopt_t(init_t& /*unused*/) {} -}; - -extern const nullopt_t nullopt; - -namespace optional_internal { - -// throw delegator -[[noreturn]] void throw_bad_optional_access(); - - -struct empty_struct {}; - -// This class stores the data in optional. -// It is specialized based on whether T is trivially destructible. -// This is the specialization for non trivially destructible type. -template ::value> -class optional_data_dtor_base -{ - struct dummy_type { - static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); - // Use an array to avoid GCC 6 placement-new warning. - empty_struct data[sizeof(T) / sizeof(empty_struct)]; - }; - -protected: - // Whether there is data or not. - bool engaged_; - // Data storage - union { - dummy_type dummy_; - T data_; - }; - - void destruct() noexcept { - if (engaged_) { - data_.~T(); - engaged_ = false; - } - } - - // dummy_ must be initialized for constexpr constructor. - constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} - - template - constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) - : engaged_(true), data_(phmap::forward(args)...) {} - - ~optional_data_dtor_base() { destruct(); } -}; - -// Specialization for trivially destructible type. -template -class optional_data_dtor_base -{ - struct dummy_type { - static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); - // Use array to avoid GCC 6 placement-new warning. - empty_struct data[sizeof(T) / sizeof(empty_struct)]; - }; - -protected: - // Whether there is data or not. - bool engaged_; - // Data storage - union { - dummy_type dummy_; - T data_; - }; - void destruct() noexcept { engaged_ = false; } - - // dummy_ must be initialized for constexpr constructor. - constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} - - template - constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) - : engaged_(true), data_(phmap::forward(args)...) {} -}; - -template -class optional_data_base : public optional_data_dtor_base -{ -protected: - using base = optional_data_dtor_base; -#if PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS - using base::base; -#else - optional_data_base() = default; - - template - constexpr explicit optional_data_base(in_place_t t, Args&&... args) - : base(t, phmap::forward(args)...) {} -#endif - - template - void construct(Args&&... args) { - // Use dummy_'s address to work around casting cv-qualified T* to void*. - ::new (static_cast(&this->dummy_)) T(std::forward(args)...); - this->engaged_ = true; - } - - template - void assign(U&& u) { - if (this->engaged_) { - this->data_ = std::forward(u); - } else { - construct(std::forward(u)); - } - } -}; - -// TODO(phmap-team): Add another class using -// std::is_trivially_move_constructible trait when available to match -// http://cplusplus.github.io/LWG/lwg-defects.html#2900, for types that -// have trivial move but nontrivial copy. -// Also, we should be checking is_trivially_copyable here, which is not -// supported now, so we use is_trivially_* traits instead. -template ::value&& - phmap::is_trivially_copy_assignable::type>::value&& std::is_trivially_destructible::value> -class optional_data; - -// Trivially copyable types -template -class optional_data : public optional_data_base -{ -protected: -#if PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS - using optional_data_base::optional_data_base; -#else - optional_data() = default; - - template - constexpr explicit optional_data(in_place_t t, Args&&... args) - : optional_data_base(t, phmap::forward(args)...) {} -#endif -}; - -template -class optional_data : public optional_data_base -{ -protected: -#if PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS - using optional_data_base::optional_data_base; -#else - template - constexpr explicit optional_data(in_place_t t, Args&&... args) - : optional_data_base(t, phmap::forward(args)...) {} -#endif - - optional_data() = default; - - optional_data(const optional_data& rhs) : optional_data_base() { - if (rhs.engaged_) { - this->construct(rhs.data_); - } - } - - optional_data(optional_data&& rhs) noexcept( - phmap::default_allocator_is_nothrow::value || - std::is_nothrow_move_constructible::value) - : optional_data_base() { - if (rhs.engaged_) { - this->construct(std::move(rhs.data_)); - } - } - - optional_data& operator=(const optional_data& rhs) { - if (rhs.engaged_) { - this->assign(rhs.data_); - } else { - this->destruct(); - } - return *this; - } - - optional_data& operator=(optional_data&& rhs) noexcept( - std::is_nothrow_move_assignable::value&& - std::is_nothrow_move_constructible::value) { - if (rhs.engaged_) { - this->assign(std::move(rhs.data_)); - } else { - this->destruct(); - } - return *this; - } -}; - -// Ordered by level of restriction, from low to high. -// Copyable implies movable. -enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 }; - -// Base class for enabling/disabling copy/move constructor. -template -class optional_ctor_base; - -template <> -class optional_ctor_base -{ -public: - constexpr optional_ctor_base() = default; - optional_ctor_base(const optional_ctor_base&) = default; - optional_ctor_base(optional_ctor_base&&) = default; - optional_ctor_base& operator=(const optional_ctor_base&) = default; - optional_ctor_base& operator=(optional_ctor_base&&) = default; -}; - -template <> -class optional_ctor_base -{ -public: - constexpr optional_ctor_base() = default; - optional_ctor_base(const optional_ctor_base&) = delete; - optional_ctor_base(optional_ctor_base&&) = default; - optional_ctor_base& operator=(const optional_ctor_base&) = default; - optional_ctor_base& operator=(optional_ctor_base&&) = default; -}; - -template <> -class optional_ctor_base -{ -public: - constexpr optional_ctor_base() = default; - optional_ctor_base(const optional_ctor_base&) = delete; - optional_ctor_base(optional_ctor_base&&) = delete; - optional_ctor_base& operator=(const optional_ctor_base&) = default; - optional_ctor_base& operator=(optional_ctor_base&&) = default; -}; - -// Base class for enabling/disabling copy/move assignment. -template -class optional_assign_base; - -template <> -class optional_assign_base -{ -public: - constexpr optional_assign_base() = default; - optional_assign_base(const optional_assign_base&) = default; - optional_assign_base(optional_assign_base&&) = default; - optional_assign_base& operator=(const optional_assign_base&) = default; - optional_assign_base& operator=(optional_assign_base&&) = default; -}; - -template <> -class optional_assign_base -{ -public: - constexpr optional_assign_base() = default; - optional_assign_base(const optional_assign_base&) = default; - optional_assign_base(optional_assign_base&&) = default; - optional_assign_base& operator=(const optional_assign_base&) = delete; - optional_assign_base& operator=(optional_assign_base&&) = default; -}; - -template <> -class optional_assign_base -{ -public: - constexpr optional_assign_base() = default; - optional_assign_base(const optional_assign_base&) = default; - optional_assign_base(optional_assign_base&&) = default; - optional_assign_base& operator=(const optional_assign_base&) = delete; - optional_assign_base& operator=(optional_assign_base&&) = delete; -}; - -template -constexpr copy_traits get_ctor_copy_traits() -{ - return std::is_copy_constructible::value - ? copy_traits::copyable - : std::is_move_constructible::value ? copy_traits::movable - : copy_traits::non_movable; -} - -template -constexpr copy_traits get_assign_copy_traits() -{ - return phmap::is_copy_assignable::value && - std::is_copy_constructible::value - ? copy_traits::copyable - : phmap::is_move_assignable::value && - std::is_move_constructible::value - ? copy_traits::movable - : copy_traits::non_movable; -} - -// Whether T is constructible or convertible from optional. -template -struct is_constructible_convertible_from_optional - : std::integral_constant< - bool, std::is_constructible&>::value || - std::is_constructible&&>::value || - std::is_constructible&>::value || - std::is_constructible&&>::value || - std::is_convertible&, T>::value || - std::is_convertible&&, T>::value || - std::is_convertible&, T>::value || - std::is_convertible&&, T>::value> {}; - -// Whether T is constructible or convertible or assignable from optional. -template -struct is_constructible_convertible_assignable_from_optional - : std::integral_constant< - bool, is_constructible_convertible_from_optional::value || - std::is_assignable&>::value || - std::is_assignable&&>::value || - std::is_assignable&>::value || - std::is_assignable&&>::value> {}; - -// Helper function used by [optional.relops], [optional.comp_with_t], -// for checking whether an expression is convertible to bool. -bool convertible_to_bool(bool); - -// Base class for std::hash>: -// If std::hash> is enabled, it provides operator() to -// compute the hash; Otherwise, it is disabled. -// Reference N4659 23.14.15 [unord.hash]. -template -struct optional_hash_base -{ - optional_hash_base() = delete; - optional_hash_base(const optional_hash_base&) = delete; - optional_hash_base(optional_hash_base&&) = delete; - optional_hash_base& operator=(const optional_hash_base&) = delete; - optional_hash_base& operator=(optional_hash_base&&) = delete; -}; - -template -struct optional_hash_base >()( - std::declval >()))> -{ - using argument_type = phmap::optional; - using result_type = size_t; - size_t operator()(const phmap::optional& opt) const { - phmap::type_traits_internal::AssertHashEnabled>(); - if (opt) { - return std::hash >()(*opt); - } else { - return static_cast(0x297814aaad196e6dULL); - } - } -}; - -} // namespace optional_internal - // ----------------------------------------------------------------------------- // file utility.h // ----------------------------------------------------------------------------- -#include -#include - // --------- identity.h namespace phmap { namespace internal { @@ -1535,6 +1146,911 @@ T exchange(T& obj, U&& new_value) } // namespace phmap +// ----------------------------------------------------------------------------- +// memory.h +// ----------------------------------------------------------------------------- + +namespace phmap { + +template +std::unique_ptr WrapUnique(T* ptr) +{ + static_assert(!std::is_array::value, "array types are unsupported"); + static_assert(std::is_object::value, "non-object types are unsupported"); + return std::unique_ptr(ptr); +} + +namespace memory_internal { + +// Traits to select proper overload and return type for `phmap::make_unique<>`. +template +struct MakeUniqueResult { + using scalar = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using array = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using invalid = void; +}; + +} // namespace memory_internal + +#if (__cplusplus > 201103L || defined(_MSC_VER)) && \ + !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8) + using std::make_unique; +#else + + template + typename memory_internal::MakeUniqueResult::scalar make_unique( + Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + typename memory_internal::MakeUniqueResult::array make_unique(size_t n) { + return std::unique_ptr(new typename phmap::remove_extent_t[n]()); + } + + template + typename memory_internal::MakeUniqueResult::invalid make_unique( + Args&&... /* args */) = delete; +#endif + +template +auto RawPtr(T&& ptr) -> decltype(std::addressof(*ptr)) +{ + // ptr is a forwarding reference to support Ts with non-const operators. + return (ptr != nullptr) ? std::addressof(*ptr) : nullptr; +} + +inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; } + +template +std::shared_ptr ShareUniquePtr(std::unique_ptr&& ptr) { + return ptr ? std::shared_ptr(std::move(ptr)) : std::shared_ptr(); +} + +template +std::weak_ptr WeakenPtr(const std::shared_ptr& ptr) { + return std::weak_ptr(ptr); +} + +namespace memory_internal { + +// ExtractOr::type evaluates to E if possible. Otherwise, D. +template