From a75f1d77e3b2464f7cc843496505a532e5d5b551 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 19 Jul 2026 19:55:05 +0200 Subject: [PATCH] WIP: trying performance improvement of two-layer DRC checks (do merge internally instead of before) --- src/db/db/dbAsIfFlatRegion.cc | 30 +++--- src/db/db/dbDeepRegion.cc | 1 + src/db/db/dbLocalOperationUtils.h | 8 +- src/db/db/dbRegionLocalOperations.cc | 131 ++++++++++++++++++++++++--- src/db/db/dbRegionLocalOperations.h | 2 +- 5 files changed, 140 insertions(+), 32 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 1886f26ab..f253f4768 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1184,13 +1184,8 @@ AsIfFlatRegion::width_check (db::Coord d, const RegionCheckOptions &options) con EdgePairsDelegate * AsIfFlatRegion::space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const { - if (options.opposite_filter != NoOppositeFilter || options.rect_filter != NoRectFilter || options.shielded) { - // NOTE: we have to use the "foreign" scheme with a filter because only this scheme - // guarantees that all subject shapes are visited. - return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options); - } else { - return run_check (db::SpaceRelation, isolated, subject_regionptr (), d, options); - } + // NOTE: we have to use "foreign" to make sure every subject sees neighboring subjects + return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options); } EdgePairsDelegate * @@ -1238,12 +1233,14 @@ AsIfFlatRegion::inside_check (const Region &other, db::Coord d, const RegionChec EdgePairsDelegate * AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const { + bool has_other = other && other != subject_regionptr () && other != foreign_regionptr (); + // force different polygons in the different properties case to skip intra-polygon checks - if (pc_always_different (options.prop_constraint)) { + if (! has_other && pc_always_different (options.prop_constraint)) { different_polygons = true; } - bool needs_merged_primary = different_polygons || options.needs_merged (); + bool needs_merged_primary = (! has_other && different_polygons) || options.needs_merged (); db::RegionIterator polygons (needs_merged_primary ? begin_merged () : begin ()); bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); @@ -1252,15 +1249,18 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, std::vector others; std::vector foreign; - bool has_other = false; bool other_is_merged = true; - if (other == subject_regionptr () || other == foreign_regionptr ()) { + if (! has_other) { + foreign.push_back (other == foreign_regionptr ()); others.push_back (polygons); other_is_merged = primary_is_merged; + } else { + foreign.push_back (false); + if (! other->merged_semantics ()) { others.push_back (other->begin ()); other_is_merged = true; @@ -1272,7 +1272,13 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, others.push_back (other->begin ()); other_is_merged = other->is_merged (); } - has_other = true; + + // adds another intruder section to implement subject merging ("primary_intruders") + if (! primary_is_merged) { + foreign.push_back (true); + others.push_back (polygons); + } + } std::unique_ptr output (new FlatEdgePairs ()); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 43eac39bf..8a984dfc7 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -2368,6 +2368,7 @@ DeepRegion::cop_to_edges (db::CompoundRegionOperationNode &node, db::PropertyCon EdgePairsDelegate * DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const { + // @@@ if (empty ()) { return new db::DeepEdgePairs (deep_layer ().derived ()); } else if (other && ! is_subject_regionptr (other) && other->empty () && ! options.negative) { diff --git a/src/db/db/dbLocalOperationUtils.h b/src/db/db/dbLocalOperationUtils.h index 815439951..c7eced15b 100644 --- a/src/db/db/dbLocalOperationUtils.h +++ b/src/db/db/dbLocalOperationUtils.h @@ -376,10 +376,10 @@ private: */ template DB_PUBLIC_TEMPLATE -std::map, std::set > > +std::map, std::set > > > separate_interactions_by_properties (const shape_interactions, db::object_with_properties > &interactions, db::PropertyConstraint property_constraint) { - std::map, std::set > > by_prop_id; + std::map, std::set > > > by_prop_id; for (auto i = interactions.begin (); i != interactions.end (); ++i) { @@ -387,7 +387,7 @@ separate_interactions_by_properties (const shape_interactions, std::set > &s2p = by_prop_id [prop_id]; + auto &s2p = by_prop_id [prop_id]; s2p.first.push_back (&subject); for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { @@ -395,7 +395,7 @@ separate_interactions_by_properties (const shape_interactions > &intruder = interactions.intruder_shape (*ii); if (pc_match (property_constraint, prop_id, intruder.second.properties_id ())) { - s2p.second.insert (&intruder.second); + s2p.second.insert (std::make_pair (intruder.first, &intruder.second)); } } diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 186ed30db..feaabf569 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -211,7 +211,7 @@ check_local_operation_base::check_local_operation_base (const EdgeRelati template void -check_local_operation_base::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const db::LocalProcessorBase *proc) const +check_local_operation_base::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &primary_intruders, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const db::LocalProcessorBase *proc) const { // NOTE: the rectangle and opposite filters are unsymmetric bool symmetric_edge_pairs = ! m_has_other && m_options.opposite_filter == db::NoOppositeFilter && m_options.rect_filter == RectFilter::NoRectFilter; @@ -229,7 +229,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel db::EdgeProcessor ep; ep.set_base_verbosity (50); - bool take_all = edge_check.has_negative_edge_output () || intruders.empty (); + bool take_all = edge_check.has_negative_edge_output () || (intruders.empty () && primary_intruders.empty ()); db::Box common_box; bool subjects_are_fully_covered = false; @@ -259,6 +259,14 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel all_intruders_box += intruder_box.enlarged (e); } + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { + db::Box intruder_box = db::box_convert () (**i); + if (! subjects_are_fully_covered && (*i)->is_box () && common_box.inside (intruder_box)) { + subjects_are_fully_covered = true; + } + all_intruders_box += intruder_box.enlarged (e); + } + common_box &= all_intruders_box; } @@ -269,7 +277,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel size_t n = 0; - if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box ())) { + if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box () && primary_intruders.empty ())) { for (auto i = subjects.begin (); i != subjects.end (); ++i) { if (! take_all) { @@ -280,9 +288,10 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel n += 2; } - } else { + } else if (primary_intruders.empty ()) { // merge needed for the subject shapes + // Here we don't have other subjects to consider. This is the simple case. ep.clear (); size_t nn = 0; @@ -310,6 +319,79 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel n += 2; } + } else { + + // merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then + // reclaim original subject edges that are "outer" edges and not covered by other subjects shapes. + + ep.clear (); + size_t nn = 0; + + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + + std::vector edges; + + db::EdgeContainer ee (edges); + db::SimpleMerge op (1 /*wc>0*/); + ep.process (ee, op); + ep.clear (); + + std::vector subject_edges; + + size_t sz = 0; + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + sz += (*i)->vertices (); + } + + subject_edges.reserve (sz); + + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { + subject_edges.push_back (*e); + } + } + + std::set partial_edges; + + EdgeBooleanClusterCollector > cluster_collector (&partial_edges, db::EdgeAnd); + + db::box_scanner scanner; + scanner.reserve (edges.size () + subject_edges.size ()); + + for (auto i = edges.begin (); i != edges.end (); ++i) { + if (! i->is_degenerate ()) { + scanner.insert (i.operator-> (), 0); + } + } + + for (auto i = subject_edges.begin (); i != subject_edges.end (); ++i) { + if (! i->is_degenerate ()) { + scanner.insert (i.operator-> (), 1); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + for (auto e = partial_edges.begin (); e != partial_edges.end (); ++e) { + if (! take_all) { + poly_check.enter (*e, 0, common_box); + } else { + poly_check.enter (*e, 0); + } + } + } // merge the intruders to remove inner edges @@ -384,11 +466,11 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel } else { - if (m_is_merged || (subjects.size () == 1 && intruders.empty () && subjects.front ()->is_box ())) { + if (m_is_merged || (subjects.size () == 1 && primary_intruders.empty () && subjects.front ()->is_box ())) { // no merge required - // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield + // NOTE: we need to eliminate identical shapes from primary intruders and subjects because those will shield size_t n = 0; std::unordered_set subjects_hash; @@ -406,7 +488,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel n = 1; - for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { if (subjects_hash.find (**i) == subjects_hash.end ()) { if (! take_all) { poly_check.enter (**i, n, common_box); @@ -416,9 +498,9 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel } } - } else if (intruders.empty ()) { + } else if (primary_intruders.empty ()) { - // merge needed for the subject shapes - no intruders present so this is the simple case + // merge needed for the subject shapes - no primary intruders ("other subjects") present so this is the simple case size_t n = 0; @@ -451,7 +533,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel } else { - // merge needed for the subject and intruder shapes - we merge both and then + // merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then // separate edges into those from the subject and those from intruder shapes. ep.clear (); @@ -464,7 +546,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel ++nn; } - for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } @@ -727,11 +809,19 @@ check_local_operation::do_compute_local (db::Layout *layout, db::Cell *s subjects.reserve (interactions.size ()); std::set intruders; + std::set primary_intruders; + + unsigned int primary_intruder_layer = check_local_operation_base::m_has_other ? 1 : 0; for (auto i = interactions.begin (); i != interactions.end (); ++i) { subjects.push_back (&interactions.subject_shape (i->first)); for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { - intruders.insert (&interactions.intruder_shape (*ii).second); + const auto &is = interactions.intruder_shape (*ii); + if (is.first == primary_intruder_layer) { + primary_intruders.insert (&is.second); + } else { + intruders.insert (&is.second); + } } } @@ -740,7 +830,7 @@ check_local_operation::do_compute_local (db::Layout *layout, db::Cell *s std::unordered_set result, intra_polygon_result; // perform the basic check - check_local_operation_base::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc); + check_local_operation_base::compute_results (layout, subject_cell, subjects, primary_intruders, intruders, result, intra_polygon_result, proc); // detect and remove parts of the result which have or do not have results "opposite" // ("opposite" is defined by the projection of edges "through" the subject shape) @@ -816,6 +906,8 @@ check_local_operation_with_properties::do_compute_local (db::Layout *lay { tl_assert (results.size () == 1); + unsigned int primary_intruder_layer = check_local_operation_base::m_has_other ? 1 : 0; + auto by_prop_id = separate_interactions_by_properties (interactions, check_local_operation_base::m_options.prop_constraint); for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { @@ -823,10 +915,18 @@ check_local_operation_with_properties::do_compute_local (db::Layout *lay std::unordered_set result, intra_polygon_result; const std::vector &subjects = s2p->second.first; - const std::set &intruders = s2p->second.second; + + std::set intruders, primary_intruders; + for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { + if (i->first == primary_intruder_layer) { + primary_intruders.insert (i->second); + } else { + intruders.insert (i->second); + } + } // perform the basic check - check_local_operation_base::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc); + check_local_operation_base::compute_results (layout, subject_cell, subjects, primary_intruders, intruders, result, intra_polygon_result, proc); // detect and remove parts of the result which have or do not have results "opposite" // ("opposite" is defined by the projection of edges "through" the subject shape) @@ -2295,6 +2395,7 @@ PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell } else { + // @@@ that's not correct, isn't it? // With intruders: to compute our local contribution we take the edges without and with intruders // and deliver what is in both sets diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index c1a4e170b..1650d6fb0 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -194,7 +194,7 @@ protected: bool m_other_is_merged; db::RegionCheckOptions m_options; - void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const LocalProcessorBase *proc) const; + void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &primary_intruders, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const LocalProcessorBase *proc) const; void apply_opposite_filter (const std::vector &subjects, std::unordered_set &result, std::unordered_set &intra_polygon_result) const; void apply_rectangle_filter (const std::vector &subjects, std::unordered_set &result) const; };