From 855e8a3518f2cb75c1d163463b2516e2544b04a0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 27 Apr 2021 22:27:22 +0200 Subject: [PATCH] WIP: some enhancements with the effect of reducing instance interaction caching overhead and improving performance. More memory and cache metrics. --- src/db/db/dbHierNetworkProcessor.cc | 159 +++++++++++++++++++++++----- src/db/db/dbHierNetworkProcessor.h | 20 +++- src/db/db/dbMemStatistics.h | 39 +++++++ 3 files changed, 193 insertions(+), 25 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index d3eac046f..31f68314c 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -44,6 +44,11 @@ namespace db // ------------------------------------------------------------------------------ +// Don't cache instance to instance interaction sets beyond this size +const size_t instance_to_instance_cache_set_size_threshold = 10000; + +// ------------------------------------------------------------------------------ + template void insert_transformed (db::Layout &layout, db::Shapes &shapes, const Shape &s, const Trans &t); template void insert_transformed (db::Layout &layout, db::Shapes &shapes, const db::PolygonRef &s, const Trans &t) @@ -311,6 +316,54 @@ bool Connectivity::interacts (const T &a, unsigned int la, const T &b, unsigned } } +bool Connectivity::interacts (const std::set &la, const std::set &lb) const +{ + for (std::set::const_iterator i = la.begin (); i != la.end (); ++i) { + db::Connectivity::layer_iterator je = end_connected (*i); + for (db::Connectivity::layer_iterator j = begin_connected (*i); j != je; ++j) { + if (lb.find (*j) != lb.end ()) { + return true; + } + } + } + + return false; +} + +bool Connectivity::interact (const db::Cell &a, const db::Cell &b) const +{ + for (std::map::const_iterator i = m_connected.begin (); i != m_connected.end (); ++i) { + if (! a.bbox (i->first).empty ()) { + for (layers_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + if (! b.bbox (*j).empty ()) { + return true; + } + } + } + } + + return false; +} + +template +bool Connectivity::interact (const db::Cell &a, const T &ta, const db::Cell &b, const T &tb) const +{ + for (std::map::const_iterator i = m_connected.begin (); i != m_connected.end (); ++i) { + db::Box ba = a.bbox (i->first); + if (! ba.empty ()) { + ba.transform (ta); + for (layers_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + db::Box bb = b.bbox (*j); + if (! bb.empty () && bb.transformed (tb).touches (ba)) { + return true; + } + } + } + } + + return false; +} + // explicit instantiations template DB_PUBLIC bool Connectivity::interacts (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::UnitTrans &trans) const; template DB_PUBLIC bool Connectivity::interacts (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::ICplxTrans &trans) const; @@ -318,6 +371,7 @@ template DB_PUBLIC bool Connectivity::interacts (const db::Polyg template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::ICplxTrans &trans) const; template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::UnitTrans &trans) const; template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::ICplxTrans &trans) const; +template DB_PUBLIC bool Connectivity::interact (const db::Cell &a, const db::ICplxTrans &ta, const db::Cell &b, const db::ICplxTrans &tb) const; // ------------------------------------------------------------------------------ // local_cluster implementation @@ -556,15 +610,7 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans return false; } - bool any = false; - - for (std::set::const_iterator i = ll1.begin (); i != ll1.end () && !any; ++i) { - db::Connectivity::layer_iterator je = conn.end_connected (*i); - for (db::Connectivity::layer_iterator j = conn.begin_connected (*i); j != je && !any; ++j) { - any = (ll2.find (*j) != ll2.end ()); - } - } - if (! any) { + if (! conn.interacts (ll1, ll2)) { return false; } @@ -1392,11 +1438,37 @@ public: * @brief Constructor */ hc_receiver (const db::Layout &layout, const db::Cell &cell, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_clusters_box_converter &cbc, const db::Connectivity &conn, const std::set *breakout_cells, typename hier_clusters::instance_interaction_cache_type *instance_interaction_cache) - : mp_layout (&layout), mp_cell (&cell), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn), mp_breakout_cells (breakout_cells), mp_instance_interaction_cache (instance_interaction_cache) + : mp_layout (&layout), mp_cell (&cell), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn), mp_breakout_cells (breakout_cells), m_cluster_cache_misses (0), m_cluster_cache_hits (0), mp_instance_interaction_cache (instance_interaction_cache) { mp_cell_clusters = &cell_clusters; } + /** + * @brief Gets the cache size + */ + size_t cluster_cache_size () const + { + db::MemStatisticsSimple ms; + ms << m_interaction_cache_for_clusters; + return ms.used (); + } + + /** + * @brief Gets the cache hits + */ + size_t cluster_cache_hits () const + { + return m_cluster_cache_hits; + } + + /** + * @brief Gets the cache misses + */ + size_t cluster_cache_misses () const + { + return m_cluster_cache_misses; + } + /** * @brief Receiver main event for instance-to-instance interactions */ @@ -1519,7 +1591,8 @@ private: std::map m_cm2join_map; join_set_list m_cm2join_sets; std::list m_ci_interactions; - std::map > > m_interaction_cache_for_clusters; + std::map > > m_interaction_cache_for_clusters; + size_t m_cluster_cache_misses, m_cluster_cache_hits; typename hier_clusters::instance_interaction_cache_type *mp_instance_interaction_cache; /** @@ -1559,13 +1632,21 @@ private: return; } + // gross shortcut: the cells do no comprise a constallation which will ever interact + if (! mp_conn->interact (mp_layout->cell (i1.cell_index ()), mp_layout->cell (i2.cell_index ()))) { + return; + } + InstanceToInstanceInteraction ii_key; db::ICplxTrans i1t, i2t; bool fill_cache = false; + size_t n1 = i1element.at_end () ? i1.size () : 1; + size_t n2 = i2element.at_end () ? i2.size () : 1; + // Cache is only used for single instances or simple and regular arrays. - if ((! i1element.at_end () || i1.size () == 1 || ! i1.is_iterated_array ()) && - (! i2element.at_end () || i2.size () == 1 || ! i2.is_iterated_array ())) { + if ((n1 == 1 || ! i1.is_iterated_array ()) && + (n2 == 1 || ! i2.is_iterated_array ())) { i1t = i1element.at_end () ? i1.complex_trans () : i1.complex_trans (*i1element); db::ICplxTrans tt1 = t1 * i1t; @@ -1595,10 +1676,35 @@ private: } + // avoid caching few-to-many interactions as this typically does not contribute anything fill_cache = true; } + // shortcut: if the instances cannot interact because their bounding boxes do no comprise a valid constellation, + // reject the pair + + bool any = false; + for (db::Connectivity::layer_iterator la = mp_conn->begin_layers (); la != mp_conn->end_layers () && ! any; ++la) { + db::box_convert bca (*mp_layout, *la); + box_type bb1 = i1.cell_inst ().bbox (bca).transformed (t1); + if (! bb1.empty ()) { + db::Connectivity::layer_iterator lbe = mp_conn->end_connected (*la); + for (db::Connectivity::layer_iterator lb = mp_conn->begin_connected (*la); lb != lbe && ! any; ++lb) { + db::box_convert bcb (*mp_layout, *lb); + box_type bb2 = i2.cell_inst ().bbox (bcb).transformed (t2); + any = bb1.touches (bb2); + } + } + } + + if (! any) { + if (fill_cache) { + mp_instance_interaction_cache->insert (ii_key); + } + return; + } + // array interactions db::ICplxTrans t1i = t1.inverted (); @@ -1625,9 +1731,9 @@ private: box_type common12 = ib1 & ib2 & common; if (! common12.empty ()) { - const std::vector > &i2i_interactions = compute_instance_interactions (common12, i1.cell_index (), tt1, i2.cell_index (), tt2); + const std::list > &i2i_interactions = compute_instance_interactions (common12, i1.cell_index (), tt1, i2.cell_index (), tt2); - for (std::vector >::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { + for (std::list >::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { ClusterInstance k1 (ii->first, i1.cell_index (), i1t, i1.prop_id ()); ClusterInstance k2 (ii->second, i2.cell_index (), i2t, i2.prop_id ()); interacting_clusters.push_back (std::make_pair (k1, k2)); @@ -1692,7 +1798,7 @@ private: // return the list of unique interactions interacting_clusters.insert (interacting_clusters.end (), sorted_interactions.begin (), sorted_interactions.end ()); - if (fill_cache) { + if (fill_cache && sorted_interactions.size () < instance_to_instance_cache_set_size_threshold) { // normalize transformations for cache db::ICplxTrans i1ti = i1t.inverted (), i2ti = i2t.inverted (); @@ -1710,13 +1816,13 @@ private: /** * @brief Computes a list of interacting clusters for two instances */ - const std::vector > & + const std::list > & compute_instance_interactions (const box_type &common, db::cell_index_type ci1, const db::ICplxTrans &t1, db::cell_index_type ci2, const db::ICplxTrans &t2) { if (is_breakout_cell (mp_breakout_cells, ci1) || is_breakout_cell (mp_breakout_cells, ci2)) { - static const std::vector > empty; + static const std::list > empty; return empty; } @@ -1728,7 +1834,7 @@ private: InteractionKeyForClustersType ikey (ci1, ci2, t1i, t21, common2); - typename std::map > >::const_iterator ici = m_interaction_cache_for_clusters.find (ikey); + typename std::map > >::const_iterator ici = m_interaction_cache_for_clusters.find (ikey); if (ici != m_interaction_cache_for_clusters.end ()) { return ici->second; @@ -1740,7 +1846,7 @@ private: const db::local_clusters &cl1 = mp_tree->clusters_per_cell (ci1); const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); - std::vector > &new_interactions = m_interaction_cache_for_clusters [ikey]; + std::list > &new_interactions = m_interaction_cache_for_clusters [ikey]; db::ICplxTrans t12 = t2i * t1; for (typename db::local_clusters::touching_iterator i = cl1.begin_touching (common2.transformed (t21)); ! i.at_end (); ++i) { @@ -1806,8 +1912,8 @@ private: cluster_instance_pair_list_type interacting_clusters; box_type common = (ib & ib2); - const std::vector > &i2i_interactions = compute_instance_interactions (common, i.cell_index (), tt, i.cell_index (), tt2); - for (std::vector >::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { + const std::list > &i2i_interactions = compute_instance_interactions (common, i.cell_index (), tt, i.cell_index (), tt2); + for (std::list >::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { ClusterInstance k1 (ii->first, i.cell_index (), tt, i.prop_id ()); ClusterInstance k2 (ii->second, i.cell_index (), tt2, i.prop_id ()); interacting_clusters.push_back (std::make_pair (k1, k2)); @@ -2236,8 +2342,8 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou build_hier_connections_for_cells (cbc, layout, todo, conn, breakout_cells, progress, instance_interaction_cache); } - if (tl::verbosity () >= 51) { - tl::info << "Cluster build cache statistics: size=" << instance_interaction_cache.size () << ", hits=" << instance_interaction_cache.hits () << ", misses=" << instance_interaction_cache.misses (); + if (tl::verbosity () >= m_base_verbosity + 20) { + tl::info << "Cluster build cache statistics (instance to instance cache): size=" << instance_interaction_cache.size () << ", hits=" << instance_interaction_cache.hits () << ", misses=" << instance_interaction_cache.misses (); } } @@ -2430,6 +2536,11 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // join local clusters which got connected by child clusters rec->finish_cluster_to_instance_interactions (); + + if (tl::verbosity () >= m_base_verbosity + 20) { + tl::info << "Cluster build cache statistics (instance to shape cache): size=" << rec->cluster_cache_size () << ", hits=" << rec->cluster_cache_hits () << ", misses=" << rec->cluster_cache_misses (); + } + rec.reset (0); // finally connect global nets diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index aeb41b006..1db70a89a 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -187,6 +187,22 @@ public: return interacts (a, la, b, lb, UnitTrans ()); } + /** + * @brief Returns true, if two sets of layers interact + */ + bool interacts (const std::set &la, const std::set &lb) const; + + /** + * @brief Returns true, if two cells basically (without considering transformation) interact + */ + bool interact (const db::Cell &a, const db::Cell &b) const; + + /** + * @brief Returns true, if two cells with the given transformations interact + */ + template + bool interact (const db::Cell &a, const T &ta, const db::Cell &b, const T &tb) const; + private: layers_type m_all_layers; std::map m_connected; @@ -1095,7 +1111,9 @@ public: size_t size () const { - return m_map.size (); + MemStatisticsSimple ms; + ms << m_map; + return ms.used (); } size_t hits () const diff --git a/src/db/db/dbMemStatistics.h b/src/db/db/dbMemStatistics.h index 8293c85bf..4ed03a805 100644 --- a/src/db/db/dbMemStatistics.h +++ b/src/db/db/dbMemStatistics.h @@ -108,6 +108,45 @@ private: std::map > m_per_purpose; }; +/** + * @brief A simple memory statistics collector + * This collector will simply add the size required + */ +class DB_PUBLIC MemStatisticsSimple + : public MemStatistics +{ +public: + MemStatisticsSimple () + : m_size (0), m_used (0) + { } + + size_t size () const + { + return m_size; + } + + size_t used () const + { + return m_used; + } + + virtual void add (const std::type_info & /*ti*/, void * /*ptr*/, size_t size, size_t used, void * /*parent*/, purpose_t /*purpose*/, int /*cat*/) + { + m_size += size; + m_used += used; + } + + template + MemStatisticsSimple &operator<< (const T &x) + { + mem_stat (this, None, 0, x); + return *this; + } + +private: + size_t m_size, m_used; +}; + // Some standard templates to collect the information template void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const X &x, bool no_self = false, void *parent = 0)