From 9e79942bb994085dbea42c1388dad58445fe941f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Jan 2026 10:06:58 +0100 Subject: [PATCH 1/4] Proposing one part of fix --- src/db/db/dbHierarchyBuilder.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index b54354858..ca49ebd98 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -390,10 +390,12 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn if (all) { CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ()); + + // NOTE: this will set m_cm_new_entry db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ())); // for new cells, create this instance - if (m_cell_stack.back ().first) { + if (m_cell_stack.back ().first || m_cm_new_entry) { db::CellInstArray new_inst (inst, &mp_target->array_repository ()); new_inst.object () = db::CellInst (new_cell); new_inst.transform (always_apply); @@ -432,10 +434,12 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second); + + // NOTE: this will set m_cm_new_entry db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell)); // for a new cell, create this instance - if (m_cell_stack.back ().first) { + if (m_cell_stack.back ().first || m_cm_new_entry) { db::CellInstArray new_inst (db::CellInst (new_cell), always_apply * trans); new_inst.transform_into (m_trans); for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { From 23b314345139f03bf6af4d458cff0cf4a1a30a14 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Jan 2026 13:36:22 +0100 Subject: [PATCH 2/4] Report flat count in deep mode, optimize hierarchy handling 1. In deep mode, the count report column will report hierarchical and flat counts 2. XOR is shortcut evaluated with one empty input. The default implementation does that internally, but at the cost of mapping to the internal working layout which spoils hierarchy. --- src/buddies/src/bd/strmxor.cc | 79 +++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 7df5be64b..6720d58e8 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -29,6 +29,7 @@ #include "dbSaveLayoutOptions.h" #include "dbRegion.h" #include "dbDeepShapeStore.h" +#include "dbCellGraphUtils.h" #include "gsiExpression.h" #include "tlCommandLineParser.h" #include "tlThreads.h" @@ -271,12 +272,13 @@ HealingTileLayoutOutputReceiver::output (const db::Box &box) struct ResultDescriptor { ResultDescriptor () - : shape_count (0), layer_a (-1), layer_b (-1), layer_output (-1), layout (0), top_cell (0) + : shape_count (0), flat_shape_count (0), layer_a (-1), layer_b (-1), layer_output (-1), layout (0), top_cell (0) { // .. nothing yet .. } size_t shape_count; + size_t flat_shape_count; int layer_a; int layer_b; int layer_output; @@ -296,6 +298,20 @@ struct ResultDescriptor } } + size_t flat_count () const + { + if (layout && layer_output >= 0) { + size_t res = 0; + db::CellCounter counter (layout, top_cell); + for (db::Layout::const_iterator c = layout->begin (); c != layout->end (); ++c) { + res += c->shapes (layer_output).size () * counter.weight (c->cell_index ()); + } + return res; + } else { + return flat_shape_count; + } + } + bool is_empty () const { if (layout && layer_output >= 0) { @@ -586,12 +602,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) const char *line_format = " %-10s %-12s %s"; - std::string headline; - if (deep) { - headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)"))); - } else { - headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)"))); - } + std::string headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)"))); const char *sep = " ----------------------------------------------------------------"; @@ -619,7 +630,11 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (r->second.layer_output >= 0 && r->second.layout) { out = r->second.layout->get_properties (r->second.layer_output).to_string (); } - value = tl::to_string (r->second.count ()); + if (deep) { + value = tl::sprintf (tl::to_string (tr ("%lu (hierarchical) %lu (flat)")), r->second.count (), r->second.flat_count ()); + } else { + value = tl::to_string (r->second.count ()); + } } if (! value.empty ()) { tl::info << tl::sprintf (line_format, r->first.second.to_string (), out, value); @@ -846,29 +861,40 @@ class XORTask tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ()); - db::RecursiveShapeIterator ri_a, ri_b; + db::Region xor_res; - if (m_la >= 0) { - ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); - } else { - ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector ()); - } - ri_a.set_for_merged_input (true); + if (m_la < 0) { + + tl_assert (m_lb >= 0); + + db::RecursiveShapeIterator ri_b (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); + xor_res = db::Region (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); + + } else if (m_lb < 0) { + + db::RecursiveShapeIterator ri_a (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); + xor_res = db::Region (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); - if (m_lb >= 0) { - ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); } else { - ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector ()); - } - ri_b.set_for_merged_input (true); - db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); - db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); + db::RecursiveShapeIterator ri_a (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); + db::RecursiveShapeIterator ri_b (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); + + db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); + db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); + + bool a_empty = in_a.empty (); + bool b_empty = in_b.empty (); + + if (a_empty && ! b_empty) { + xor_res = in_b; + } else if (! a_empty && b_empty) { + xor_res = in_a; + } else if (! a_empty && ! b_empty) { + tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ()); + xor_res = in_a ^ in_b; + } - db::Region xor_res; - { - tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ()); - xor_res = in_a ^ in_b; } int tol_index = 0; @@ -899,6 +925,7 @@ class XORTask xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output); } else { result.shape_count = xor_res.hier_count (); + result.flat_shape_count = xor_res.count (); } } From 9e0572b86337a5432becad4e517c084dbbc29f74 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Jan 2026 19:37:13 +0100 Subject: [PATCH 3/4] Delayed generation of outputs In the multi-threaded case this saves some time. Still for the sample from the issue that is a dominant runtime component and makes a 8-core run slower than the single-core one. --- src/buddies/src/bd/strmxor.cc | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 6720d58e8..0330eec48 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -35,6 +35,7 @@ #include "tlThreads.h" #include "tlThreadedWorkers.h" #include "tlTimer.h" +#include "tlOptional.h" namespace { @@ -284,6 +285,7 @@ struct ResultDescriptor int layer_output; db::Layout *layout; db::cell_index_type top_cell; + tl::optional results; size_t count () const { @@ -782,8 +784,15 @@ bool run_tiled_xor (const XORData &xor_data) proc.execute ("Running XOR"); } + // no stored results currently + for (std::map, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end (); ++r) { + tl_assert (! r->second.results.has_value ()); + } + // Determines the output status for (std::map, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) { + // no stored results currently + tl_assert (! r->second.results.has_value ()); result = r->second.is_empty (); } @@ -922,7 +931,9 @@ class XORTask if (mp_xor_data->output_layout) { result.layer_output = result.layout->insert_layer (lp); - xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output); + if (! xor_res.empty ()) { + result.results = xor_res; + } } else { result.shape_count = xor_res.hier_count (); result.flat_shape_count = xor_res.count (); @@ -991,6 +1002,22 @@ bool run_deep_xor (const XORData &xor_data) job.start (); job.wait (); + // Deliver the outputs + // NOTE: this is done single-threaded and in a delayed fashion as it is not efficient during + // computation and shifting hierarchy of the working layout + + if (xor_data.output_layout) { + + tl::SelfTimer timer (tl::verbosity () >= 11, "Result delivery"); + + for (std::map, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end (); ++r) { + if (r->second.results.has_value ()) { + r->second.results.value ().insert_into (xor_data.output_layout, xor_data.output_cell, r->second.layer_output); + } + } + + } + // Determine the output status bool result = (xor_data.layers_missing == 0); From d326df24247f7321350a80f9713c39b4de1147ad Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Jan 2026 21:58:44 +0100 Subject: [PATCH 4/4] Updating shape count output format and tests --- src/buddies/src/bd/strmxor.cc | 5 ++-- src/buddies/unit_tests/bdStrmxorTests.cc | 30 ++++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 0330eec48..c19dae98e 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -604,7 +604,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) const char *line_format = " %-10s %-12s %s"; - std::string headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)"))); + std::string headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), + deep ? tl::to_string (tr ("Differences (hierarchical/flat count)")) : tl::to_string (tr ("Differences (shape count)"))); const char *sep = " ----------------------------------------------------------------"; @@ -633,7 +634,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) out = r->second.layout->get_properties (r->second.layer_output).to_string (); } if (deep) { - value = tl::sprintf (tl::to_string (tr ("%lu (hierarchical) %lu (flat)")), r->second.count (), r->second.flat_count ()); + value = tl::sprintf (tl::to_string (tr ("%-6lu / %-6lu")), r->second.count (), r->second.flat_count ()); } else { value = tl::to_string (r->second.count ()); } diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index bb06a9428..caec430a4 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -146,11 +146,11 @@ TEST(1A_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (hierarchical shape count)\n" + " Layer Output Differences (hierarchical/flat count)\n" " ----------------------------------------------------------------\n" - " 3/0 3/0 3\n" - " 6/0 6/0 314\n" - " 8/1 8/1 1\n" + " 3/0 3/0 3 / 30 \n" + " 6/0 6/0 314 / 314 \n" + " 8/1 8/1 1 / 1 \n" " 10/0 - (no such layer in first layout)\n" "\n" ); @@ -188,11 +188,11 @@ TEST(1A_DeepNoEmptyCells) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (hierarchical shape count)\n" + " Layer Output Differences (hierarchical/flat count)\n" " ----------------------------------------------------------------\n" - " 3/0 3/0 3\n" - " 6/0 6/0 314\n" - " 8/1 8/1 1\n" + " 3/0 3/0 3 / 30 \n" + " 6/0 6/0 314 / 314 \n" + " 8/1 8/1 1 / 1 \n" " 10/0 - (no such layer in first layout)\n" "\n" ); @@ -248,11 +248,11 @@ TEST(1B_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (hierarchical shape count)\n" + " Layer Output Differences (hierarchical/flat count)\n" " ----------------------------------------------------------------\n" - " 3/0 - 3\n" - " 6/0 - 314\n" - " 8/1 - 1\n" + " 3/0 - 3 / 30 \n" + " 6/0 - 314 / 314 \n" + " 8/1 - 1 / 1 \n" " 10/0 - (no such layer in first layout)\n" "\n" ); @@ -830,10 +830,10 @@ TEST(7_OptimizeDeep) EXPECT_EQ (cap.captured_text (), "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (hierarchical shape count)\n" + " Layer Output Differences (hierarchical/flat count)\n" " ----------------------------------------------------------------\n" - " 2/0 2/0 1\n" - " 3/0 3/0 8\n" + " 2/0 2/0 1 / 12 \n" + " 3/0 3/0 8 / 8 \n" "\n" ); }