From d058aaf673e0862f518eb80135de41c6a724d66c Mon Sep 17 00:00:00 2001 From: khizmax Date: Sun, 8 Mar 2015 18:41:41 +0300 Subject: [PATCH] Added more measures to sync monitor statistics --- cds/sync/monitor.h | 30 ++++------------------------ cds/sync/pool_monitor.h | 24 +++++++++++++++++----- tests/unit/map2/map_defs.h | 20 +++++++++---------- tests/unit/print_sync_monitor_stat.h | 4 +++- 4 files changed, 35 insertions(+), 43 deletions(-) diff --git a/cds/sync/monitor.h b/cds/sync/monitor.h index 60d3afcd..09eb1970 100644 --- a/cds/sync/monitor.h +++ b/cds/sync/monitor.h @@ -1,18 +1,12 @@ //$$CDS-header$$ - #ifndef CDSLIB_SYNC_MONITOR_H #define CDSLIB_SYNC_MONITOR_H - #include - namespace cds { namespace sync { - /** @page cds_sync_monitor Synchronization monitor - A monitor is synchronization construct that allows threads to have both mutual exclusion and the ability to wait (block) for a certain condition to become true. - Some blocking data structure algoritms like the trees require per-node locking. For huge trees containing millions of nodes it can be very inefficient to inject the lock object into each node. Moreover, some operating systems may not support @@ -22,9 +16,7 @@ namespace cds { namespace sync { lock object for the node if needed, and to lock the node. The monitor strategy can significantly reduce the number of system objects required for data structure. - Implemetations - \p libcds contains several monitor implementations: - \p sync::injecting_monitor injects the lock object into each node. That mock monitor is designed for user-space locking primitive like @@ -33,15 +25,12 @@ namespace cds { namespace sync { for a node from the pool when needed. When the node is unlocked the lock assigned to it is given back to the pool if no thread references to that node. - How to use - If you use a container from \p libcds that requires a monitor, you should just - specify required monitor type in container's traits. Usually, the monitor + specify required monitor type in container's traits. Usually, the monitor is specified by \p traits::sync_monitor typedef, or by \p cds::opt::sync_monitor option for container's \p make_traits metafunction. - - If you're developing a new container algorithm, you should know internal monitor + If you're developing a new container algorithm, you should know internal monitor interface: \code class Monitor { @@ -51,23 +40,19 @@ namespace cds { namespace sync { struct node_injection: public Node { // Monitor data to inject into container's node - // ... + // ... }; - - // Locks the node + // Locks the node template void lock( Node& node ); - // Unlocks the node template void unlock( Node& node ); - // Scoped lock applyes RAII to Monitor template using scoped_lock = monitor_scoped_lock< pool_monitor, Node >; }; \endcode - Monitor's data must be inject into container's node as \p m_SyncMonitorInjection data member: \code template @@ -77,7 +62,6 @@ namespace cds { namespace sync { typename SyncMonitor::node_injection m_SyncMonitorInjection; }; \endcode - The monitor must be a member of your container: \code template @@ -90,7 +74,6 @@ namespace cds { namespace sync { }; \endcode */ - /// Monitor scoped lock (RAII) /** Template arguments: @@ -103,13 +86,11 @@ namespace cds { namespace sync { public: typedef Monitor monitor_type; ///< Monitor type typedef Node node_type; ///< Node type - private: //@cond monitor_type& m_Monitor; ///< Monitor node_type const& m_Node; ///< Our locked node //@endcond - public: /// Makes exclusive access to the node \p p by \p monitor monitor_scoped_lock( monitor_type& monitor, node_type const& p ) @@ -118,15 +99,12 @@ namespace cds { namespace sync { { monitor.lock( p ); } - /// Unlocks the node ~monitor_scoped_lock() { m_Monitor.unlock( m_Node ); } }; - }} // namespace cds::sync - #endif // #ifndef CDSLIB_SYNC_MONITOR_H diff --git a/cds/sync/pool_monitor.h b/cds/sync/pool_monitor.h index 404c35d2..8045a59b 100644 --- a/cds/sync/pool_monitor.h +++ b/cds/sync/pool_monitor.h @@ -34,17 +34,31 @@ namespace cds { namespace sync { event_counter m_nLockCount; ///< Number of monitor \p lock() call event_counter m_nUnlockCount; ///< Number of monitor \p unlock call + event_counter m_nMaxLocked; ///< Max number of simuntaneously locked mutexes event_counter m_nLockContention; ///< Number of \p lock() contenton event_counter m_nUnlockContention; ///< Number of \p unlock() contention event_counter m_nLockAllocation; ///< Number of the lock allocation from the pool event_counter m_nLockDeallocation; ///< Number of the lock deallocation + event_counter m_nMaxAllocated; ///< Max number of sumultanouusly allocated mutexes //@cond - void onLock() { ++m_nLockCount; } + void onLock() + { + ++m_nLockCount; + int nDiff = static_cast( m_nLockCount.get() - m_nUnlockCount.get()); + if ( nDiff > 0 && m_nMaxLocked.get() < static_cast( nDiff )) + m_nMaxLocked = static_cast( nDiff ); + } void onUnlock() { ++m_nUnlockCount; } void onLockContention() { ++m_nLockContention; } void onUnlockContention() { ++m_nUnlockContention;} - void onLockAllocation() { ++m_nLockAllocation; } + void onLockAllocation() + { + ++m_nLockAllocation; + int nDiff = static_cast( m_nLockAllocation.get() - m_nLockDeallocation.get()); + if ( nDiff > 0 && m_nMaxAllocated.get() < static_cast( nDiff ) ) + m_nMaxAllocated = static_cast( nDiff ); + } void onLockDeallocation() { ++m_nLockDeallocation;} //@endcond }; @@ -65,7 +79,7 @@ namespace cds { namespace sync { Template arguments: - \p LockPool - the @ref cds_memory_pool "pool type". The pool must maintain the objects of type \p std::mutex or similar. The access to the pool is not synchronized. - - \p BackOff - back-off strategy for spinning, default is \p cds::backoff::LockDefault + - \p BackOff - back-off strategy for spinning, default is \p cds::backoff::yield - \p Stat - enable (\p true) or disable (\p false, the default) monitor's internal statistics. How to use @@ -74,7 +88,7 @@ namespace cds { namespace sync { typedef cds::sync::pool_monitor< pool_type > sync_monitor; \endcode */ - template + template class pool_monitor { public: @@ -82,7 +96,7 @@ namespace cds { namespace sync { typedef typename pool_type::value_type lock_type; ///< node lock type typedef typename std::conditional< std::is_same< BackOff, cds::opt::none >::value, - cds::backoff::LockDefault, + cds::backoff::yield, BackOff >::type back_off; ///< back-off strategy for spinning typedef uint32_t refspin_type; ///< Reference counter + spin-lock bit diff --git a/tests/unit/map2/map_defs.h b/tests/unit/map2/map_defs.h index 99e3003a..089a64d4 100644 --- a/tests/unit/map2/map_defs.h +++ b/tests/unit/map2/map_defs.h @@ -458,16 +458,16 @@ TEST_MAP_EXTRACT(SplitList_Lazy_RCU_SHT_st_less_stat) CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_cmp_stat) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_cmp_stat) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_simple) \ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_simple) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_simple_stat) \ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_simple) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_simple_stat) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_lazy) \ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_lazy) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_lazy_stat) \ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_lazy) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_lazy_stat) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_bounded) \ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_bounded) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_bounded_stat) \ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_bounded) \ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_bounded_stat) \ #else @@ -507,25 +507,23 @@ TEST_MAP_EXTRACT(SplitList_Lazy_RCU_SHT_st_less_stat) CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_cmp_stat)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_cmp_stat)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_cmp_stat)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_simple)\ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_cmp_stat)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_simple)\ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_cmp_stat)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_simple)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_simple_stat)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_simple_stat)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_simple_stat)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_lazy)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_lazy)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_lazy)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_lazy_stat)\ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_lazy)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_lazy_stat)\ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_lazy)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_lazy_stat)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_bounded)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_bounded)\ - CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_bounded)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_bounded_stat)\ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_bounded)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_bounded_stat)\ + CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_bounded)\ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_bounded_stat)\ CDSUNIT_TEST_BronsonAVLTreeMap_RCU_signal diff --git a/tests/unit/print_sync_monitor_stat.h b/tests/unit/print_sync_monitor_stat.h index 721ddcda..aa56cecf 100644 --- a/tests/unit/print_sync_monitor_stat.h +++ b/tests/unit/print_sync_monitor_stat.h @@ -25,10 +25,12 @@ namespace std { return o << "cds::sync::pool_monitor statistics:\n" << "\t\t m_nLockCount: " << s.m_nLockCount.get() << "\n" << "\t\t m_nUnlockCount: " << s.m_nUnlockCount.get() << "\n" + << "\t\t m_nMaxLocked: " << s.m_nMaxLocked.get() << "\n" << "\t\t m_nLockContention: " << s.m_nLockContention.get() << "\n" << "\t\t m_nUnlockContention: " << s.m_nUnlockContention.get() << "\n" << "\t\t m_nLockAllocation: " << s.m_nLockAllocation.get() << "\n" - << "\t\t m_nLockDeallocation: " << s.m_nLockDeallocation.get() << "\n"; + << "\t\t m_nLockDeallocation: " << s.m_nLockDeallocation.get() << "\n" + << "\t\t m_nMaxAllocated: " << s.m_nMaxAllocated.get() << "\n"; } } #endif -- 2.34.1