Refined XOR optimization solution such that it is compatible with deep mode and 'wants_all_cells', added more tests

This commit is contained in:
Matthias Koefferlein 2024-03-28 20:57:10 +01:00
parent cb5a1f7d3e
commit 7080ed9a0c
4 changed files with 148 additions and 34 deletions

View File

@ -82,6 +82,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
m_cells = d.m_cells;
m_local_complex_region_stack = d.m_local_complex_region_stack;
m_local_region_stack = d.m_local_region_stack;
m_skip_shapes_stack = d.m_skip_shapes_stack;
m_needs_reinit = d.m_needs_reinit;
m_inst_quad_id = d.m_inst_quad_id;
m_inst_quad_id_stack = d.m_inst_quad_id_stack;
@ -462,6 +463,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const
m_local_region_stack.clear ();
m_local_region_stack.push_back (m_global_trans.inverted () * m_region);
m_skip_shapes_stack.clear ();
m_skip_shapes_stack.push_back (false);
m_local_complex_region_stack.clear ();
if (mp_complex_region.get ()) {
@ -736,9 +739,23 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const
}
if (is_empty || !down (receiver)) {
if (is_empty) {
// skip entire cell
++m_inst;
new_inst (receiver);
} else if (!down (receiver)) {
// skip this instance array member
++m_inst_array;
new_inst_member (receiver);
if (m_inst_array.at_end ()) {
++m_inst;
new_inst (receiver);
}
}
} else {
@ -769,6 +786,39 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const
bool
RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
{
bool skip_shapes = false;
if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) {
// Try some optimization: if the instance we're looking at is entirely covered
// by a rectangle (other objects are too expensive to check), then we skip it
//
// We check 10 shapes max.
box_type inst_bx;
if (m_inst->size () == 1) {
inst_bx = m_inst->bbox (m_box_convert);
} else {
inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ());
}
unsigned int l = m_has_layers ? m_layers.front () : m_layer;
auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
size_t nmax = 10;
while (! si.at_end () && nmax-- > 0) {
if (inst_bx.inside (si->rectangle ())) {
skip_shapes = true;
break;
}
++si;
}
}
if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) {
return false;
}
tl_assert (mp_layout);
m_trans_stack.push_back (m_trans);
@ -796,6 +846,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
}
m_local_region_stack.push_back (new_region);
m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes);
if (! m_local_complex_region_stack.empty ()) {
@ -878,6 +929,7 @@ RecursiveShapeIterator::pop () const
mp_cell = m_cells.back ();
m_cells.pop_back ();
m_local_region_stack.pop_back ();
m_skip_shapes_stack.pop_back ();
if (! m_local_complex_region_stack.empty ()) {
m_local_complex_region_stack.pop_back ();
}
@ -902,7 +954,7 @@ RecursiveShapeIterator::start_shapes () const
void
RecursiveShapeIterator::new_layer () const
{
if (int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
m_shape = shape_iterator ();
} else if (! m_overlapping) {
m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
@ -942,7 +994,7 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
m_inst_quad_id = 0;
// skip instance quad if possible
if (! m_local_complex_region_stack.empty ()) {
if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) {
skip_inst_iter_for_complex_region ();
}
@ -958,40 +1010,13 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
while (! m_inst.at_end ()) {
// skip instance quad if possible
if (! m_local_complex_region_stack.empty ()) {
if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) {
skip_inst_iter_for_complex_region ();
if (m_inst.at_end ()) {
break;
}
}
if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1)) {
// Try some optimization: if the instance we're looking at is entirely covered
// by a rectangle (other objects are too expensive to check), then wil skip it
//
// We check 10 shapes max.
unsigned int l = m_has_layers ? m_layers.front () : m_layer;
box_type inst_bx = m_inst->bbox (m_box_convert);
auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
bool skip = false;
size_t nmax = 10;
while (! skip && ! si.at_end () && nmax-- > 0) {
if (inst_bx.inside (si->rectangle ())) {
skip = true;
break;
}
++si;
}
if (skip) {
++m_inst;
continue;
}
}
bool all_of_instance = false;
bool with_region = false;

View File

@ -867,6 +867,7 @@ private:
mutable std::vector<const cell_type *> m_cells;
mutable std::vector<box_tree_type> m_local_complex_region_stack;
mutable std::vector<box_type> m_local_region_stack;
mutable std::vector<bool> m_skip_shapes_stack;
mutable bool m_needs_reinit;
mutable size_t m_inst_quad_id;
mutable std::vector<size_t> m_inst_quad_id_stack;

View File

@ -23,6 +23,7 @@
#include "tlUnitTest.h"
#include "tlStream.h"
#include "tlFileUtils.h"
#include "dbHierProcessor.h"
#include "dbTestSupport.h"
#include "dbReader.h"
@ -32,6 +33,9 @@
#include "dbLocalOperationUtils.h"
#include "dbRegionLocalOperations.h"
#include "dbPolygon.h"
#include "dbRecursiveInstanceIterator.h"
#include "dbDeepShapeStore.h"
#include "dbRegion.h"
static std::string testdata (const std::string &fn)
{
@ -1284,3 +1288,87 @@ TEST(Arrays)
run_test_bool2 (_this, "hlp18.oas", TMNot, 100);
}
TEST(XORTool)
{
test_is_long_runner ();
std::string fna (tl::combine_path (tl::testdata_private (), "xor/a.gds.gz"));
std::string fnb (tl::combine_path (tl::testdata_private (), "xor/b.gds.gz"));
std::string fn_au (tl::combine_path (tl::testdata_private (), "xor/xor_au.oas.gz"));
db::Layout lya, lyb;
unsigned int l1, l2;
db::LayerMap lmap;
lmap.map (db::LDPair (1, 0), l1 = lya.insert_layer ());
lyb.insert_layer ();
lmap.map (db::LDPair (2, 0), l2 = lya.insert_layer ());
lyb.insert_layer ();
{
tl::InputStream stream (fna);
db::Reader reader (stream);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
reader.read (lya, options);
}
{
tl::InputStream stream (fnb);
db::Reader reader (stream);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
reader.read (lyb, options);
}
db::Layout ly_out;
db::cell_index_type top_out = ly_out.add_cell ("TOP");
unsigned int l1_out = ly_out.insert_layer (db::LayerProperties (1, 0));
unsigned int l2_out = ly_out.insert_layer (db::LayerProperties (2, 0));
db::DeepShapeStore dss;
dss.set_wants_all_cells (true); // saves time for less cell mapping operations
{
db::RecursiveShapeIterator ri_a, ri_b;
ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l1);
ri_a.set_for_merged_input (true);
ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l1);
ri_b.set_for_merged_input (true);
db::Region in_a (ri_a, dss, db::ICplxTrans (1.0));
db::Region in_b (ri_b, dss, db::ICplxTrans (1.0));
db::Region xor_res = in_a ^ in_b;
EXPECT_EQ (xor_res.count (), size_t (12));
xor_res.insert_into (&ly_out, top_out, l1_out);
}
{
db::RecursiveShapeIterator ri_a, ri_b;
ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l2);
ri_a.set_for_merged_input (true);
ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l2);
ri_b.set_for_merged_input (true);
db::Region in_a (ri_a, dss, db::ICplxTrans (1.0));
db::Region in_b (ri_b, dss, db::ICplxTrans (1.0));
db::Region xor_res = in_a ^ in_b;
EXPECT_EQ (xor_res.count (), size_t (15984));
xor_res.insert_into (&ly_out, top_out, l2_out);
}
db::compare_layouts (_this, ly_out, fn_au, db::WriteOAS);
}

View File

@ -1683,7 +1683,7 @@ TEST(13_ForMergedPerformance)
++n;
}
tl::info << "Counted " << n << " shapes on 66/20";
EXPECT_EQ (n, size_t (1212844));
EXPECT_EQ (n, size_t (1203078));
}
{
@ -1735,7 +1735,7 @@ TEST(13_ForMergedPerformance)
++n;
}
tl::info << "Counted " << n << " shapes on 66/20";
EXPECT_EQ (n, size_t (218552));
EXPECT_EQ (n, size_t (218069));
}
{
@ -1757,7 +1757,7 @@ TEST(13_ForMergedPerformance)
db::Region r2 (si1);
EXPECT_EQ (r1.count (), size_t (218823));
EXPECT_EQ (r2.count (), size_t (218552));
EXPECT_EQ (r2.count (), size_t (218069));
EXPECT_EQ ((r1 ^ r2).count (), size_t (0));
}