diff --git a/.gitmodules b/.gitmodules index c28bb72c3..55ea47e8d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -23,3 +23,6 @@ [submodule "third_party/wide-integer"] path = third_party/wide-integer url = ../../ckormanyos/wide-integer.git +[submodule "third_party/parallel-hashmap"] + path = third_party/parallel-hashmap + url = ../../greg7mdp/parallel-hashmap.git diff --git a/libgringo/CMakeLists.txt b/libgringo/CMakeLists.txt index b9a60a717..c0c935d63 100644 --- a/libgringo/CMakeLists.txt +++ b/libgringo/CMakeLists.txt @@ -126,7 +126,7 @@ set(source # ]]] add_library(libgringo STATIC ${header} ${source}) -target_link_libraries(libgringo PUBLIC libpotassco libreify tsl::ordered_map tsl::hopscotch_map tsl::sparse_map tl::optional mpark::variant math::wide_integer) +target_link_libraries(libgringo PUBLIC libpotassco libreify tsl::ordered_map tsl::hopscotch_map tsl::sparse_map tl::optional mpark::variant math::wide_integer grep::phmap) target_include_directories(libgringo PUBLIC "$" diff --git a/libgringo/gringo/shared_mutex.hh b/libgringo/gringo/shared_mutex.hh new file mode 100644 index 000000000..0a48a7b5b --- /dev/null +++ b/libgringo/gringo/shared_mutex.hh @@ -0,0 +1,154 @@ +#pragma once + +#if defined(_WIN32) || defined(_WIN64) +#define GRINGO_PLATFORM_WINDOWS 1 +#else +#define GRINGO_PLATFORM_WINDOWS 0 +#endif + +#if !GRINGO_PLATFORM_WINDOWS && (defined(__unix__) || defined(__APPLE__)) +#define GRINGO_PLATFORM_POSIX 1 +#else +#define GRINGO_PLATFORM_POSIX 0 +#endif + +#include +#include +#include +#include + +#if GRINGO_PLATFORM_WINDOWS +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#elif GRINGO_PLATFORM_POSIX +#include +#endif + +#ifndef PHMAP_DISABLE_MIX +#define PHMAP_DISABLE_MIX 1 +#endif +#include + +namespace Gringo { + +#if GRINGO_PLATFORM_POSIX +namespace Detail { + +inline void check_pthread_call(char const *fun, int code) noexcept { + if (code != 0) { + std::fprintf(stderr, "fatal: %s failed: %s\n", fun, std::strerror(code)); + std::abort(); + } +} + +} // namespace Detail +#endif + +class SharedMutex { + public: + SharedMutex() noexcept { +#if GRINGO_PLATFORM_WINDOWS + InitializeSRWLock(&srw_); +#elif GRINGO_PLATFORM_POSIX + Detail::check_pthread_call("pthread_rwlock_init", pthread_rwlock_init(&rw_, nullptr)); +#endif + } + + ~SharedMutex() noexcept { +#if GRINGO_PLATFORM_POSIX + Detail::check_pthread_call("pthread_rwlock_destroy", pthread_rwlock_destroy(&rw_)); +#endif + } + + SharedMutex(const SharedMutex &) = delete; + SharedMutex &operator=(const SharedMutex &) = delete; + SharedMutex(SharedMutex &&) = delete; + SharedMutex &operator=(SharedMutex &&) = delete; + + void lock_shared() noexcept { +#if GRINGO_PLATFORM_WINDOWS + AcquireSRWLockShared(&srw_); +#elif GRINGO_PLATFORM_POSIX + Detail::check_pthread_call("pthread_rwlock_rdlock", pthread_rwlock_rdlock(&rw_)); +#else + mtx_.lock_shared(); +#endif + } + + bool try_lock_shared() noexcept { +#if GRINGO_PLATFORM_WINDOWS + return TryAcquireSRWLockShared(&srw_) != 0; +#elif GRINGO_PLATFORM_POSIX + return pthread_rwlock_tryrdlock(&rw_) == 0; +#else + return mtx_.try_lock_shared(); +#endif + } + + void unlock_shared() noexcept { +#if GRINGO_PLATFORM_WINDOWS + ReleaseSRWLockShared(&srw_); +#elif GRINGO_PLATFORM_POSIX + Detail::check_pthread_call("pthread_rwlock_unlock", pthread_rwlock_unlock(&rw_)); +#else + mtx_.unlock_shared(); +#endif + } + + void lock() noexcept { +#if GRINGO_PLATFORM_WINDOWS + AcquireSRWLockExclusive(&srw_); +#elif GRINGO_PLATFORM_POSIX + Detail::check_pthread_call("pthread_rwlock_wrlock", pthread_rwlock_wrlock(&rw_)); +#else + mtx_.lock(); +#endif + } + + bool try_lock() noexcept { +#if GRINGO_PLATFORM_WINDOWS + return TryAcquireSRWLockExclusive(&srw_) != 0; +#elif GRINGO_PLATFORM_POSIX + return pthread_rwlock_trywrlock(&rw_) == 0; +#else + return mtx_.try_lock(); +#endif + } + + void unlock() noexcept { +#if GRINGO_PLATFORM_WINDOWS + ReleaseSRWLockExclusive(&srw_); +#elif GRINGO_PLATFORM_POSIX + Detail::check_pthread_call("pthread_rwlock_unlock", pthread_rwlock_unlock(&rw_)); +#else + mtx_.unlock(); +#endif + } + + private: +#if GRINGO_PLATFORM_WINDOWS + SRWLOCK srw_; +#elif GRINGO_PLATFORM_POSIX + pthread_rwlock_t rw_; +#else + std::shared_timed_mutex mtx_; +#endif +}; + +} // namespace Gringo + +namespace phmap { + +template <> class LockableImpl : public Gringo::SharedMutex { + public: + using mutex_type = Gringo::SharedMutex; + using Base = LockableBaseImpl; + using SharedLock = std::shared_lock; + using ReadWriteLock = typename Base::ReadWriteLock; + using UniqueLock = std::unique_lock; + using SharedLocks = typename Base::ReadLocks; + using UniqueLocks = typename Base::WriteLocks; +}; + +} // namespace phmap diff --git a/libgringo/src/symbol.cc b/libgringo/src/symbol.cc index 0da667d31..30ea57b0d 100644 --- a/libgringo/src/symbol.cc +++ b/libgringo/src/symbol.cc @@ -24,10 +24,15 @@ #include #include + +#ifndef PHMAP_DISABLE_MIX +#define PHMAP_DISABLE_MIX 1 +#endif +#include + #include +#include #include -#include -#include #ifdef _MSC_VER #pragma warning(disable : 4200) // nonstandard extension used: zero-sized array in struct/union @@ -79,28 +84,24 @@ String toString(uint64_t rep) { return String::fromRep(ptr(rep)); } template struct UniqueConstruct { public: - using Set = hash_set; + using Set = phmap::parallel_flat_hash_set, 4, + Gringo::SharedMutex>; template static T const &construct(U &&x) { - // TODO: in C++17 this can use a read/write lock to not block reading threads - size_t hash = typename T::Hash{}(x); - std::lock_guard g(mutex_); + auto hash = set_.hash(x); auto it = set_.find(x, hash); - if (it != set_.end()) { + if (it != end(set_)) { return *it; } - return *set_.insert(T{std::forward(x), hash}).first; + return *set_.emplace_with_hash(hash, std::forward(x), hash).first; } private: - static Set set_; // NOLINT - static std::mutex mutex_; // NOLINT + static Set set_; // NOLINT }; template typename UniqueConstruct::Set UniqueConstruct::set_; // NOLINT -template typename std::mutex UniqueConstruct::mutex_; // NOLINT - template T const &construct_unique(U &&x) { return UniqueConstruct::construct(std::forward(x)); } @@ -112,6 +113,7 @@ class MSig { using Cons = std::pair; struct Hash { + using is_transparent = void; size_t operator()(MSig const &sig) const { return sig.hash_; } size_t operator()(Cons const &sig) const { return hash_mix(get_value_hash(sig)); } }; @@ -190,6 +192,7 @@ class MFun { using Cons = std::pair; struct Hash { + using is_transparent = void; size_t operator()(MFun const &a) const { return a.fun_->hash(); } size_t operator()(Cons const &a) const { return hash_mix(get_value_hash(a.first, hash_range(begin(a.second), end(a.second)))); @@ -285,6 +288,7 @@ class String::Impl { class String::Impl::MString { public: struct Hash { + using is_transparent = void; size_t operator()(MString const &str) const { return str.str_->hash(); } size_t operator()(char const *str) const { return hash_mix(strhash(str)); } size_t operator()(StringSpan const &str) const { return hash_mix(strhash(str)); } diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 57bdd993a..ad7d86a4c 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -30,3 +30,7 @@ add_library(mpark::variant ALIAS variant) add_library(wide_integer INTERFACE) target_include_directories(wide_integer INTERFACE $) add_library(math::wide_integer ALIAS wide_integer) + +add_library(phmap INTERFACE) +target_include_directories(phmap INTERFACE $) +add_library(grep::phmap ALIAS phmap) diff --git a/third_party/parallel-hashmap b/third_party/parallel-hashmap new file mode 160000 index 000000000..8442f1c82 --- /dev/null +++ b/third_party/parallel-hashmap @@ -0,0 +1 @@ +Subproject commit 8442f1c82cad04c026e3db4959c6b7a5396f982a