Skip to content

dpl: make optimize_mirroring deterministic#10573

Open
oharboe wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
oharboe:dpl-optmirror-deterministic
Open

dpl: make optimize_mirroring deterministic#10573
oharboe wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
oharboe:dpl-optmirror-deterministic

Conversation

@oharboe
Copy link
Copy Markdown
Collaborator

@oharboe oharboe commented May 31, 2026

net_box_map_ is a std::unordered_map<dbNet*, NetBox>, so the
sorted_boxes vector built from it starts in heap-address order. The
comparator keys on hpwl() alone and std::ranges::sort is not stable,
so equal-hpwl boxes keep that non-deterministic order. The greedy
mirrorCandidates() then mutates boxes in that order, so the mirroring
result — and the written db — differs run to run.

Tie-break the sort by net id for a stable total order.

src/dpl/test mirror1/2/3 output is unchanged.

net_box_map_ is an unordered_map, so equal-hpwl boxes sorted in
heap-address order (ranges::sort is not stable), making the mirroring
result and the written db non-deterministic. Tie-break by net id.

Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe oharboe requested a review from a team as a code owner May 31, 2026 18:45
@oharboe oharboe requested a review from osamahammad21 May 31, 2026 18:45
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the sorting logic for net boxes in OptimizeMirroring::run by introducing a tie-breaker based on the net ID when the HPWL values are equal, ensuring stable and deterministic output. There are no review comments, and I have no feedback to provide.

@oharboe
Copy link
Copy Markdown
Collaborator Author

oharboe commented May 31, 2026

While here, I swept the codebase for the same shape: a non-stable sort whose comparator is not a strict total order (single key, ties possible), over a sequence whose order is itself non-deterministic — built by iterating a pointer-keyed unordered_map/unordered_set, or tie-broken on a raw pointer. Most sort sites are fine (they key on a unique id/name, or the input is an ordered container). The ones below are not.

Likely bugs

rszbuffer_fast_sizes_ is a std::unordered_set<LibertyCell*>; it is copied into buffer_sizes_ and sorted by input capacitance only. A greedy walk then takes the first cell above a threshold, so among equal-Cin cells the choice is heap-order-dependent and the written netlist varies run to run.

std::ranges::sort(buffer_sizes_,
[=](sta::LibertyCell* a, sta::LibertyCell* b) {
return bufferCin(a) < bufferCin(b);
});

std::ranges::sort(buffer_sizes_, [=](BufferSize a, BufferSize b) {
return bufferCin(a.cell) < bufferCin(b.cell);
});

odb DEF writer — obstruction sort has a copy/paste typo: layer_b is computed from bbox_a (L1141), so the layer key is dead and equal-rect obstructions on different layers reorder in the emitted DEF.

std::ranges::sort(sorted_obs, [](dbObstruction* a, dbObstruction* b) {
dbBox* bbox_a = a->getBBox();
dbTechLayer* layer_a = bbox_a->getTechLayer();
dbBox* bbox_b = b->getBBox();
dbTechLayer* layer_b = bbox_a->getTechLayer();
if (layer_a != layer_b) {
return layer_a->getNumber() < layer_b->getNumber();
}
Rect rect_a = bbox_a->getBox();
Rect rect_b = bbox_b->getBox();

Suspects (single-key comparator feeding output; only deterministic if the input source happens to be ordered today)

DEF blockage sort — keyed on rect only, no unique tie-break:

std::ranges::sort(sorted_blockages, [](dbBlockage* a, dbBlockage* b) {
dbBox* bbox_a = a->getBBox();
dbBox* bbox_b = b->getBBox();
Rect rect_a = bbox_a->getBox();
Rect rect_b = bbox_b->getBox();

DEF sortedSet — keyed on name only (relies on names being unique):

std::sort(sorted.begin(), sorted.end(), [](T* a, T* b) {

dft scan replacement — candidates gathered from an unordered_set<LibertyCell*>, sorted by |Δ drive resistance| only, then .at(0) is substituted into the netlist:

std::ranges::sort(
scan_candidates, [&non_scan_cell](const auto& lhs, const auto& rhs) {
// We want to keep the difference as close as possible to
// the non_scan_cell
const double difference_lhs
= DifferencePerformanceCells(non_scan_cell, lhs->getScanCell());
const double difference_rhs
= DifferencePerformanceCells(non_scan_cell, rhs->getScanCell());
return difference_lhs < difference_rhs;
});
return std::move(scan_candidates.at(0));

cts dummy-cell pick — sorted by input cap only; source is STA library iteration:

std::ranges::sort(
dummyCandidates,
[](const sta::LibertyCell* cell1, const sta::LibertyCell* cell2) {
return (getInputCap(cell1) < getInputCap(cell2));
});

grt congested-NDR-net order — sorted by edge count only; feeds rip-up/reroute selection:

void Graph2D::sortCongestedNDRnets()
{
std::ranges::sort(congested_ndrs_, NDRCongestionComparator());

par port sort — std::ranges::sort on vector<pair<string, PortDirection*>> falls back to comparing the PortDirection* pointer on equal names:

std::ranges::sort(ports);

For reference, dbNetwork.cc is the correct pattern — same unordered_set<dbNet*> source, made deterministic by sorting on the unique getId():

flat_nets_vec_.assign(flat_nets_set.begin(), flat_nets_set.end());
// Sort these nets so their order is determinisitc
std::ranges::sort(
flat_nets_vec_, {}, [](auto const* net1) { return net1->getId(); });

@oharboe
Copy link
Copy Markdown
Collaborator Author

oharboe commented May 31, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the sorting logic for sorted_boxes in OptimizeMirroring::run within src/dpl/src/OptMirror.cpp. It introduces a tie-breaker using the net ID when the HPWL values are equal, ensuring stable sorting output since the underlying map is unordered. There are no review comments, so I have no feedback to provide.

Comment thread src/dpl/src/OptMirror.cpp
});
std::ranges::sort(
sorted_boxes, [](NetBox* net_box1, NetBox* net_box2) -> bool {
if (net_box1->hpwl() != net_box2->hpwl()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you might want to do:

const auto hpwl1 = net_box1->hpwl();
const auto hpwl2 = net_box2->hpwl();
if (hpwl1  != hpwl2 ) {
  return hpwl1 > hpwl2;
}

To avoid recomputing the hpwl twice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants