Performance enhancements for two-layer DRC checks

The main optimization was introducing "distance overrides"
in the local processor. That means, the primary shapes
mixed in for local merge are collected with distance 0,
while the shapes from the other layer are collected with
the check distance.

Some more optimizations involve early shortcuts in some
cases.
This commit is contained in:
Matthias Koefferlein 2026-07-23 22:26:57 +02:00
parent 1e02ffe539
commit 189ba72666
5 changed files with 111 additions and 21 deletions

View File

@ -622,21 +622,28 @@ public:
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result), m_rt (subject_layout)
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), m_override_distance (override_distance), mp_result (result), m_rt (subject_layout)
{
// nothing yet ..
}
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer)
{
collect_instance_shape_interactions (inst, layer, *ref, m_dist);
db::Coord d = m_dist;
auto od = m_override_distance.find (layer);
if (od != m_override_distance.end ()) {
d = std::min (d, od->second);
}
collect_instance_shape_interactions (inst, layer, *ref, d);
}
private:
db::Layout *mp_subject_layout;
unsigned int m_subject_layer;
db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
interactions_type *mp_result;
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> m_rt;
@ -681,11 +688,11 @@ private:
// LocalProcessorContextComputationTask implementation
template <class TS, class TI, class TR>
local_processor_context_computation_task<TS, TI, TR>::local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist)
local_processor_context_computation_task<TS, TI, TR>::local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance)
: tl::Task (),
mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context),
mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst),
mp_intruder_cell (intruder_cell), m_dist (dist)
mp_intruder_cell (intruder_cell), m_dist (dist), m_override_distance (override_distance)
{
// This is quick, but will take away the intruders from the caller
m_intruders.swap (intruders);
@ -695,7 +702,7 @@ template <class TS, class TI, class TR>
void
local_processor_context_computation_task<TS, TI, TR>::perform ()
{
mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist);
mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist, m_override_distance);
}
// ---------------------------------------------------------------------------------------------
@ -900,8 +907,18 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
contexts.set_intruder_layers (intruder_layers);
contexts.set_subject_layer (subject_layer);
// collect override distances per layer ID
auto od = op->override_distance ();
std::map<unsigned int, db::Coord> override_distance;
for (auto il = intruder_layers.begin (); il != intruder_layers.end (); ++il) {
auto o = od.find ((unsigned int) (il - intruder_layers.begin ()));
if (o != od.end ()) {
override_distance [*il] = o->second;
}
}
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type intruders;
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ());
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist (), override_distance);
if (mp_cc_job.get ()) {
mp_cc_job->start ();
@ -922,14 +939,15 @@ void local_processor<TS, TI, TR>::issue_compute_contexts (local_processor_contex
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist) const
db::Coord dist,
const std::map<unsigned int, db::Coord> &override_distance) const
{
bool is_small_job = subject_cell->begin ().at_end ();
if (! is_small_job && mp_cc_job.get ()) {
mp_cc_job->schedule (new local_processor_context_computation_task<TS, TI, TR> (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist));
mp_cc_job->schedule (new local_processor_context_computation_task<TS, TI, TR> (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist, override_distance));
} else {
compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist);
compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist, override_distance);
}
}
@ -941,7 +959,8 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist_top) const
db::Coord dist_top,
const std::map<unsigned int, db::Coord> &override_distance) const
{
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
@ -1041,10 +1060,16 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
// TODO: can we shortcut this if interactions is empty?
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
auto od = override_distance.find (*il);
db::Coord d = dist;
if (od != override_distance.end ()) {
d = std::min (d, od->second);
}
db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), d, &interactions);
unsigned int id = 0;
@ -1095,7 +1120,7 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
db::addressable_object_from_shape<TI> heap;
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, override_distance, &interactions);
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
@ -1127,7 +1152,7 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist);
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist, override_distance);
}
@ -1386,7 +1411,8 @@ template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const
{
db::Coord dist = dist_for_cell (subject_cell->cell_index (), op->dist ());
auto override_distance = op->override_distance ();
db::Coord dist_global = dist_for_cell (subject_cell->cell_index (), op->dist ());
const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ());
db::shape_to_object<TS> s2o;
@ -1412,6 +1438,13 @@ local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_conte
unsigned int il_index = 0;
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il, ++il_index) {
// compute effective distance
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, dist_for_cell (subject_cell->cell_index (), od->second));
}
unsigned int ail = contexts.actual_intruder_layer (*il);
bool foreign = contexts.is_foreign (*il);
@ -1633,15 +1666,16 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
// build the subjects in the intruders list
db::Coord dist = op->dist ();
db::Coord dist_global = op->dist ();
auto override_distance = op->override_distance ();
db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist, dist);
db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist_global, dist_global);
db::Box intruders_box;
for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) {
intruders_box += il->bbox ();
}
intruders_box = safe_box_enlarged (intruders_box, dist, dist);
intruders_box = safe_box_enlarged (intruders_box, dist_global, dist_global);
db::Box common_box = intruders_box & subjects_box;
if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) {
@ -1670,6 +1704,12 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
bool ff = foreign.size () > il_index && foreign [il_index];
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, od->second);
}
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
@ -1736,6 +1776,12 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
bool ff = foreign.size () > il_index && foreign [il_index];
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, od->second);
}
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
@ -1789,6 +1835,7 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
}
}
}
}

View File

@ -359,7 +359,7 @@ class DB_PUBLIC local_processor_context_computation_task
: public tl::Task
{
public:
local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist);
local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance);
void perform ();
private:
@ -372,6 +372,7 @@ private:
const db::Cell *mp_intruder_cell;
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type m_intruders;
db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
};
template <class TS, class TI, class TR>
@ -578,8 +579,8 @@ private:
void next () const;
size_t get_progress () const;
void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, Coord> &override_distance) const;
void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, Coord> &override_distance) const;
void compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const;
bool subject_cell_is_breakout (db::cell_index_type ci) const

View File

@ -118,6 +118,14 @@ public:
*/
virtual db::Coord dist () const { return 0; }
/**
* @brief Gets a override distance for a specific input layer
* The input layers are numbered from 0 to n-1, where n is the allowed number of intruder layers
* The override distance is used insted of the global distance ("dist()") for the given
* input layer. The override distance cannot be bigger than the global distance.
*/
virtual std::map<unsigned int, db::Coord> override_distance () const { return std::map<unsigned int, db::Coord> (); }
/**
* @brief Gets the cell variant reducer that indicates whether to build cell variants and which
*/

View File

@ -813,8 +813,27 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
unsigned int primary_intruder_layer = check_local_operation_base<TS, TI>::m_has_other ? 1 : 0;
// in the case of a two-layer check, return if there is no second layer shape
if (check_local_operation_base<TS, TI>::m_has_other) {
bool any = false;
for (auto i = interactions.begin (); i != interactions.end () && ! any; ++i) {
for (auto ii = i->second.begin (); ii != i->second.end () && ! any; ++ii) {
const auto &is = interactions.intruder_shape (*ii);
any = (is.first != primary_intruder_layer);
}
}
if (! any) {
return;
}
}
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) {
const auto &is = interactions.intruder_shape (*ii);
if (is.first == primary_intruder_layer) {
@ -823,6 +842,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
intruders.insert (&is.second);
}
}
}
tl_assert (results.size () == 1);
@ -863,6 +883,19 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
}
}
template <class TS, class TI>
std::map<unsigned int, db::Coord>
check_local_operation<TS, TI>::override_distance () const
{
// makes sure, the "foreign"-type pseudo-intruder used for merging only
// does not use the full search range, but only "touching".
std::map<unsigned int, db::Coord> od;
if (check_local_operation_base<TS, TI>::m_has_other) {
od.insert (std::make_pair (1, 0));
}
return od;
}
template <class TS, class TI>
db::Coord
check_local_operation<TS, TI>::dist () const

View File

@ -207,6 +207,7 @@ public:
check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options);
virtual db::Coord dist () const;
virtual std::map<unsigned int, db::Coord> override_distance () const;
virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
virtual bool requests_single_subjects () const { return true; }
virtual std::string description () const;