diff --git a/benchmark/bench.cc b/benchmark/bench.cc index 7e6eb24..8430c38 100644 --- a/benchmark/bench.cc +++ b/benchmark/bench.cc @@ -361,12 +361,13 @@ void memlog() // -------------------------------------------------------------------------- int main(int argc, char ** argv) { - int64_t num_keys = atoi(argv[1]); int64_t i, value = 0; if(argc <= 2) return 1; + int64_t num_keys = atoi(argv[1]); + hash_t hash; str_hash_t str_hash; diff --git a/examples/bench.cc b/examples/bench.cc index 20d02a8..83ca520 100644 --- a/examples/bench.cc +++ b/examples/bench.cc @@ -17,12 +17,13 @@ // Abseil's mutexes are very efficient (at least on windows) #include "absl/synchronization/mutex.h" #define MTX absl::Mutex - #elif 0 + #elif 1 #include - #include #if 1 + #include #define MTX boost::mutex // faster if all we do is exclusive locks like this bench #else + #include #define MTX boost::upgrade_mutex #endif #elif 1 @@ -51,7 +52,7 @@ #define MAPNAME phmap::parallel_flat_hash_map #define NMSP phmap - #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 @@ -98,6 +99,9 @@ using std::vector; int64_t _abs(int64_t x) { return (x < 0) ? -x : x; } +#ifdef _MSC_VER + #pragma warning(disable : 4996) +#endif // _MSC_VER // -------------------------------------------------------------------------- class Timer @@ -384,11 +388,15 @@ void memlog() // -------------------------------------------------------------------------- int main(int argc, char ** argv) { - int64_t num_keys = atoi(argv[1]); + int64_t num_keys = 100000000; + const char *bench_name = "random"; int64_t i, value = 0; - if(argc <= 2) - return 1; + if(argc > 2) + { + num_keys = atoi(argv[1]); + bench_name = argv[2]; + } hash_t hash; str_hash_t str_hash; @@ -406,27 +414,25 @@ int main(int argc, char ** argv) try { - if(!strcmp(argv[2], "sequential")) + if(!strcmp(bench_name, "sequential")) { for(i = 0; i < num_keys; i++) hash.insert(hash_t::value_type(i, value)); } #if 0 - else if(!strcmp(argv[2], "random")) + else if(!strcmp(bench_name, "random")) { vector v(num_keys); timer = _fill_random(v, hash); out("random", num_keys, timer); } #endif - else if(!strcmp(argv[2], "random")) + else if(!strcmp(bench_name, "random")) { fprintf(stderr, "size = %zu\n", sizeof(hash)); timer = _fill_random2(num_keys, hash); - //out("random", num_keys, timer); - //fprintf(stderr, "inserted %llu\n", hash.size()); - } - else if(!strcmp(argv[2], "lookup")) + } + else if(!strcmp(bench_name, "lookup")) { vector v(num_keys); size_t num_present; @@ -434,22 +440,22 @@ int main(int argc, char ** argv) timer = _lookup(v, hash, num_present); //fprintf(stderr, "found %zu\n", num_present); } - else if(!strcmp(argv[2], "delete")) + else if(!strcmp(bench_name, "delete")) { vector v(num_keys); timer = _delete(v, hash); } - else if(!strcmp(argv[2], "sequentialstring")) + else if(!strcmp(bench_name, "sequentialstring")) { for(i = 0; i < num_keys; i++) str_hash.insert(str_hash_t::value_type(new_string_from_integer(i), value)); } - else if(!strcmp(argv[2], "randomstring")) + else if(!strcmp(bench_name, "randomstring")) { for(i = 0; i < num_keys; i++) str_hash.insert(str_hash_t::value_type(new_string_from_integer((int)rand()), value)); } - else if(!strcmp(argv[2], "deletestring")) + else if(!strcmp(bench_name, "deletestring")) { for(i = 0; i < num_keys; i++) str_hash.insert(str_hash_t::value_type(new_string_from_integer(i), value)); diff --git a/examples/knucleotide.cc b/examples/knucleotide.cc index 7d494e3..ee3561b 100644 --- a/examples/knucleotide.cc +++ b/examples/knucleotide.cc @@ -193,7 +193,7 @@ void WriteFrequencies(const Cfg::Data& input) for(const auto& i: frequencies) freq.insert({i.second, i.first}); - const unsigned sum = input.size() + 1 - size; + const unsigned sum = (unsigned)input.size() + 1 - size; for(const auto& i : freq) std::cout << i.second << ' ' << (sum ? double(100 * i.first) / sum : 0.0) << '\n'; std::cout << '\n'; @@ -213,8 +213,8 @@ int main() Cfg::Data data; std::array buf; - while(fgets(buf.data(), buf.size(), stdin) && memcmp(">THREE", buf.data(), 6)); - while(fgets(buf.data(), buf.size(), stdin) && buf.front() != '>') { + while(fgets(buf.data(), (int)buf.size(), stdin) && memcmp(">THREE", buf.data(), 6)); + while(fgets(buf.data(), (int)buf.size(), stdin) && buf.front() != '>') { if(buf.front() != ';'){ auto i = std::find(buf.begin(), buf.end(), '\n'); data.insert(data.end(), buf.begin(), i); diff --git a/parallel_hashmap/phmap.h b/parallel_hashmap/phmap.h index e500743..f91ecc2 100644 --- a/parallel_hashmap/phmap.h +++ b/parallel_hashmap/phmap.h @@ -2551,7 +2551,7 @@ protected: { bool operator==(const Inner& o) const { - phmap::scoped_lock l(const_cast(*this), const_cast(o)); + typename Lockable::SharedLocks l(const_cast(*this), const_cast(o)); return set_ == o.set_; } @@ -3160,7 +3160,7 @@ public: { for (size_t i=0; i l(sets_[i], src.sets_[i]); + typename Lockable::UniqueLocks l(sets_[i], src.sets_[i]); sets_[i].set_.merge(src.sets_[i].set_); } } @@ -3190,7 +3190,7 @@ public: using std::swap; for (size_t i=0; i l(sets_[i], that.sets_[i]); + typename Lockable::UniqueLocks l(sets_[i], that.sets_[i]); swap(sets_[i].set_, that.sets_[i].set_); } } diff --git a/parallel_hashmap/phmap_base.h b/parallel_hashmap/phmap_base.h index 0db1875..21d2804 100644 --- a/parallel_hashmap/phmap_base.h +++ b/parallel_hashmap/phmap_base.h @@ -4507,11 +4507,7 @@ inline T& ts_unchecked_read(T& v) PHMAP_NO_THREAD_SAFETY_ANALYSIS { namespace phmap { -#if defined(BOOST_THREAD_SHARED_MUTEX_HPP) && defined(BOOST_THREAD_LOCK_TYPES_HPP) - #define PHMAP_HAS_BOOST_THREAD_MUTEXES -#endif - -#ifdef PHMAP_HAS_BOOST_THREAD_MUTEXES +#ifdef BOOST_THREAD_LOCK_OPTIONS_HPP using defer_lock_t = boost::defer_lock_t; using try_to_lock_t = boost::try_to_lock_t; using adopt_lock_t = boost::adopt_lock_t; @@ -4540,49 +4536,21 @@ public: bool try_lock_shared() { return true; } }; -// --------------------------- simplified scoped_lock ------------------------------ -template -class scoped_lock -{ -public: - using mutex_type = MutexType; - - explicit scoped_lock(mutex_type& m1, mutex_type& m2) : - _m1(m1), _m2(m2) - { - std::lock(m1, m2); - } - - scoped_lock(adopt_lock_t, mutex_type& m1, mutex_type& m2) : - _m1(m1), _m2(m2) - { // adopt means we already own the mutexes - } - - ~scoped_lock() - { - _m1.unlock(); - _m2.unlock(); - } - - scoped_lock(scoped_lock const&) = delete; - scoped_lock& operator=(scoped_lock const&) = delete; -private: - mutex_type& _m1; - mutex_type& _m2; -}; - // ------------------------ lockable object used internally ------------------------- -template +template class LockableBaseImpl { public: + // ---------------------------------------------------- struct DoNothing { + using mutex_type = MutexType; DoNothing() noexcept {} - explicit DoNothing(Mutex& ) noexcept {} - DoNothing(Mutex&, phmap::adopt_lock_t) noexcept {} - DoNothing(Mutex&, phmap::defer_lock_t) noexcept {} - DoNothing(Mutex&, phmap::try_to_lock_t) {} + explicit DoNothing(mutex_type& ) noexcept {} + explicit DoNothing(mutex_type& , mutex_type&) noexcept {} + DoNothing(mutex_type&, phmap::adopt_lock_t) noexcept {} + DoNothing(mutex_type&, phmap::defer_lock_t) noexcept {} + DoNothing(mutex_type&, phmap::try_to_lock_t) {} template explicit DoNothing(T&&) {} DoNothing& operator=(const DoNothing&) { return *this; } DoNothing& operator=(DoNothing&&) { return *this; } @@ -4590,27 +4558,28 @@ public: bool owns_lock() const noexcept { return true; } }; + // ---------------------------------------------------- class WriteLock { public: - typedef Mutex mutex_type; + using mutex_type = MutexType; WriteLock() : m_(nullptr), locked_(false) {} - explicit WriteLock(Mutex &m) : m_(&m) { + explicit WriteLock(mutex_type &m) : m_(&m) { m_->lock(); locked_ = true; } - WriteLock(Mutex& m, adopt_lock_t) noexcept : + WriteLock(mutex_type& m, adopt_lock_t) noexcept : m_(&m), locked_(true) {} - WriteLock(Mutex& m, defer_lock_t) noexcept : + WriteLock(mutex_type& m, defer_lock_t) noexcept : m_(&m), locked_(false) {} - WriteLock(Mutex& m, try_to_lock_t) : + WriteLock(mutex_type& m, try_to_lock_t) : m_(&m), locked_(false) { m_->try_lock(); } @@ -4660,34 +4629,35 @@ public: std::swap(locked_, o.locked_); } - Mutex *mutex() const noexcept { return m_; } + mutex_type *mutex() const noexcept { return m_; } private: - Mutex *m_; - bool locked_; + mutex_type *m_; + bool locked_; }; + // ---------------------------------------------------- class ReadLock { public: - typedef Mutex mutex_type; + using mutex_type = MutexType; ReadLock() : m_(nullptr), locked_(false) {} - explicit ReadLock(Mutex &m) : m_(&m) { + explicit ReadLock(mutex_type &m) : m_(&m) { m_->lock_shared(); locked_ = true; } - ReadLock(Mutex& m, adopt_lock_t) noexcept : + ReadLock(mutex_type& m, adopt_lock_t) noexcept : m_(&m), locked_(true) {} - ReadLock(Mutex& m, defer_lock_t) noexcept : + ReadLock(mutex_type& m, defer_lock_t) noexcept : m_(&m), locked_(false) {} - ReadLock(Mutex& m, try_to_lock_t) : + ReadLock(mutex_type& m, try_to_lock_t) : m_(&m), locked_(false) { m_->try_lock_shared(); } @@ -4737,11 +4707,72 @@ public: std::swap(locked_, o.locked_); } - Mutex *mutex() const noexcept { return m_; } + mutex_type *mutex() const noexcept { return m_; } private: - Mutex *m_; - bool locked_; + mutex_type *m_; + bool locked_; + }; + + // ---------------------------------------------------- + class WriteLocks + { + public: + using mutex_type = MutexType; + + explicit WriteLocks(mutex_type& m1, mutex_type& m2) : + _m1(m1), _m2(m2) + { + std::lock(m1, m2); + } + + WriteLocks(adopt_lock_t, mutex_type& m1, mutex_type& m2) : + _m1(m1), _m2(m2) + { // adopt means we already own the mutexes + } + + ~WriteLocks() + { + _m1.unlock(); + _m2.unlock(); + } + + WriteLocks(WriteLocks const&) = delete; + WriteLocks& operator=(WriteLocks const&) = delete; + private: + mutex_type& _m1; + mutex_type& _m2; + }; + + // ---------------------------------------------------- + class ReadLocks + { + public: + using mutex_type = MutexType; + + explicit ReadLocks(mutex_type& m1, mutex_type& m2) : + _m1(m1), _m2(m2) + { + _m1.lock_shared(); + _m2.lock_shared(); + } + + ReadLocks(adopt_lock_t, mutex_type& m1, mutex_type& m2) : + _m1(m1), _m2(m2) + { // adopt means we already own the mutexes + } + + ~ReadLocks() + { + _m1.unlock_shared(); + _m2.unlock_shared(); + } + + ReadLocks(ReadLocks const&) = delete; + ReadLocks& operator=(ReadLocks const&) = delete; + private: + mutex_type& _m1; + mutex_type& _m2; }; }; @@ -4771,6 +4802,8 @@ public: using SharedLock = typename Base::WriteLock; using UpgradeLock = typename Base::WriteLock; using UniqueLock = typename Base::WriteLock; + using SharedLocks = typename Base::WriteLocks; + using UniqueLocks = typename Base::WriteLocks; using UpgradeToUnique = typename Base::DoNothing; // we already have unique ownership }; @@ -4787,6 +4820,8 @@ public: using UpgradeLock = typename Base::DoNothing; using UniqueLock = typename Base::DoNothing; using UpgradeToUnique = typename Base::DoNothing; + using SharedLocks = typename Base::DoNothing; + using UniqueLocks = typename Base::DoNothing; }; // -------------------------------------------------------------------------- @@ -4813,6 +4848,8 @@ public: using SharedLock = typename Base::ReadLock; using UpgradeLock = typename Base::WriteLock; using UniqueLock = typename Base::WriteLock; + using SharedLocks = typename Base::ReadLocks; + using UniqueLocks = typename Base::WriteLocks; using UpgradeToUnique = typename Base::DoNothing; // we already have unique ownership }; @@ -4821,7 +4858,7 @@ public: // -------------------------------------------------------------------------- // Boost shared_mutex support (read and write lock support) // -------------------------------------------------------------------------- -#ifdef PHMAP_HAS_BOOST_THREAD_MUTEXES +#ifdef BOOST_THREAD_SHARED_MUTEX_HPP #if 1 // --------------------------------------------------------------------------- @@ -4834,6 +4871,8 @@ public: using SharedLock = boost::shared_lock; using UpgradeLock = boost::unique_lock; // assume can't upgrade using UniqueLock = boost::unique_lock; + using SharedLocks = typename Base::ReadLocks; + using UniqueLocks = typename Base::WriteLocks; using UpgradeToUnique = typename Base::DoNothing; // we already have unique ownership }; #else @@ -4846,6 +4885,8 @@ public: using SharedLock = boost::shared_lock; using UpgradeLock = boost::upgrade_lock; using UniqueLock = boost::unique_lock; + using SharedLocks = typename Base::ReadLocks; + using UniqueLocks = typename Base::WriteLocks; using UpgradeToUnique = boost::upgrade_to_unique_lock; }; #endif diff --git a/tests/raw_hash_set_test.cc b/tests/raw_hash_set_test.cc index 7a58f12..e17f0c0 100644 --- a/tests/raw_hash_set_test.cc +++ b/tests/raw_hash_set_test.cc @@ -30,6 +30,10 @@ #include #include +#ifdef _MSC_VER + #pragma warning(disable : 4018 4244 4702) +#endif // _MSC_VER + #if PHMAP_HAVE_STD_STRING_VIEW #include #endif @@ -795,7 +799,7 @@ TEST(Table, RehashWithNoResize) { TEST(Table, InsertEraseStressTest) { IntTable t; const size_t kMinElementCount = 250; - std::deque keys; + std::deque keys; size_t i = 0; for (; i < MaxDensitySize(kMinElementCount); ++i) { t.emplace(i); @@ -981,8 +985,8 @@ struct ProbeStats { // Fraction of elements with specified probe length. std::vector ProbeNormalizedHistogram() const { - double total_elements = std::accumulate(all_probes_histogram.begin(), - all_probes_histogram.end(), 0ull); + double total_elements = (double)std::accumulate(all_probes_histogram.begin(), + all_probes_histogram.end(), 0ull); std::vector res; for (size_t p : all_probes_histogram) { res.push_back(p / total_elements);