PathGroup use BoundedHeap

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2026-03-09 10:15:53 -07:00
parent 9b2bdf85e1
commit f1b33edd98
8 changed files with 91 additions and 133 deletions

View File

@ -60,7 +60,6 @@ public:
comp_(comp),
min_heap_comp_(comp)
{
heap_.reserve(max_size);
}
// Copy constructor
@ -107,7 +106,12 @@ public:
setMaxSize(size_t max_size)
{
max_size_ = max_size;
heap_.reserve(max_size);
}
void
reserve(size_t size)
{
heap_.reserve(size);
}
// Insert an element into the heap.
@ -172,8 +176,6 @@ public:
{
// Convert heap to sorted vector (best to worst)
std::sort_heap(heap_.begin(), heap_.end(), min_heap_comp_);
// Reverse to get best first (according to user's comparison)
std::reverse(heap_.begin(), heap_.end());
std::vector<T> result = std::move(heap_);
heap_.clear();
return result;
@ -181,11 +183,10 @@ public:
// Extract all elements sorted from best to worst (const version).
// Creates a copy since we can't modify the heap.
std::vector<T> extract() const
std::vector<T> contents() const
{
std::vector<T> temp_heap = heap_;
std::sort_heap(temp_heap.begin(), temp_heap.end(), min_heap_comp_);
std::reverse(temp_heap.begin(), temp_heap.end());
return temp_heap;
}
@ -245,7 +246,7 @@ private:
Compare comp_;
explicit MinHeapCompare(const Compare& c) : comp_(c) {}
bool operator()(const T& a, const T& b) const {
return comp_(b, a); // Inverted: worst is at root
return comp_(a, b); // comp = less puts largest at root (worst)
}
};

View File

@ -153,9 +153,13 @@ public:
static bool less(const PathEnd *path_end1,
const PathEnd *path_end2,
// Compare slack (if constrained), or arrival when false.
bool cmp_slack,
const StaState *sta);
static int cmp(const PathEnd *path_end1,
const PathEnd *path_end2,
// Compare slack (if constrained), or arrival when false.
bool cmp_slack,
const StaState *sta);
static int cmpSlack(const PathEnd *path_end1,
const PathEnd *path_end2,
@ -611,11 +615,13 @@ protected:
class PathEndLess
{
public:
PathEndLess(const StaState *sta);
PathEndLess(bool cmp_slack,
const StaState *sta);
bool operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const;
protected:
bool cmp_slack_;
const StaState *sta_;
};
@ -623,11 +629,13 @@ protected:
class PathEndSlackLess
{
public:
PathEndSlackLess(const StaState *sta);
PathEndSlackLess(bool cmp_slack,
const StaState *sta);
bool operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const;
protected:
bool cmp_slack_;
const StaState *sta_;
};

View File

@ -29,10 +29,12 @@
#include <map>
#include <mutex>
#include "BoundedHeap.hh"
#include "SdcClass.hh"
#include "StaState.hh"
#include "SearchClass.hh"
#include "StringUtil.hh"
#include "PathEnd.hh"
namespace sta {
@ -48,7 +50,6 @@ using PathGroupSeq = std::vector<PathGroup*>;
class PathGroup
{
public:
~PathGroup();
// Path group that compares compare slacks.
static PathGroup *makePathGroupArrival(const char *name,
int group_path_count,
@ -68,7 +69,7 @@ public:
const StaState *sta);
const std::string &name() const { return name_; }
const MinMax *minMax() const { return min_max_;}
const PathEndSeq &pathEnds() const { return path_ends_; }
PathEndSeq pathEnds() const;
void insert(PathEnd *path_end);
// Push group_path_count into path_ends.
void pushEnds(PathEndSeq &path_ends);
@ -76,15 +77,14 @@ public:
bool saveable(PathEnd *path_end);
bool enumMinSlackUnderMin(PathEnd *path_end);
int maxPaths() const { return group_path_count_; }
PathEndSeq &pathEnds() { return path_ends_; }
// This does NOT delete the path ends.
void clear();
static size_t group_path_count_max;
static int group_path_count_max;
protected:
PathGroup(const char *name,
size_t group_path_count,
size_t endpoint_path_count,
int group_path_count,
int endpoint_path_count,
bool unique_pins,
bool unique_edges,
float min_slack,
@ -92,21 +92,17 @@ protected:
bool cmp_slack,
const MinMax *min_max,
const StaState *sta);
void ensureSortedMaxPaths();
void prune();
void sort();
std::string name_;
size_t group_path_count_;
size_t endpoint_path_count_;
int group_path_count_;
int endpoint_path_count_;
bool unique_pins_;
bool unique_edges_;
float slack_min_;
float slack_max_;
PathEndSeq path_ends_;
const MinMax *min_max_;
bool compare_slack_;
float threshold_;
bool cmp_slack_;
BoundedHeap<PathEnd*, PathEndLess> heap_;
std::mutex lock_;
const StaState *sta_;
};

View File

@ -100,8 +100,8 @@ public:
bool unconstrained,
const SceneSeq &scenes,
const MinMaxAll *min_max,
size_t group_path_count,
size_t endpoint_path_count,
int group_path_count,
int endpoint_path_count,
bool unique_pins,
bool unique_edges,
float slack_min,

View File

@ -2030,32 +2030,22 @@ PathEndPathDelay::exceptPathCmp(const PathEnd *path_end,
////////////////////////////////////////////////////////////////
PathEndLess::PathEndLess(const StaState *sta) :
sta_(sta)
{
}
bool
PathEndLess::operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const
{
return PathEnd::less(path_end1, path_end2, sta_);
}
bool
PathEnd::less(const PathEnd *path_end1,
const PathEnd *path_end2,
bool cmp_slack,
const StaState *sta)
{
return cmp(path_end1, path_end2, sta) < 0;
return cmp(path_end1, path_end2, cmp_slack, sta) < 0;
}
int
PathEnd::cmp(const PathEnd *path_end1,
const PathEnd *path_end2,
bool cmp_slack,
const StaState *sta)
{
int cmp = path_end1->isUnconstrained()
int cmp = !cmp_slack || path_end1->isUnconstrained()
? -cmpArrival(path_end1, path_end2, sta)
: cmpSlack(path_end1, path_end2, sta);
if (cmp == 0) {
@ -2139,7 +2129,25 @@ PathEnd::cmpNoCrpr(const PathEnd *path_end1,
////////////////////////////////////////////////////////////////
PathEndSlackLess::PathEndSlackLess(const StaState *sta) :
PathEndLess::PathEndLess(bool cmp_slack,
const StaState *sta) :
cmp_slack_(cmp_slack),
sta_(sta)
{
}
bool
PathEndLess::operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const
{
return PathEnd::less(path_end1, path_end2, cmp_slack_, sta_);
}
////////////////////////////////////////////////////////////////
PathEndSlackLess::PathEndSlackLess(bool cmp_slack,
const StaState *sta) :
cmp_slack_(cmp_slack),
sta_(sta)
{
}

View File

@ -91,7 +91,7 @@ DiversionGreater::operator()(Diversion *div1,
{
PathEnd *path_end1 = div1->pathEnd();
PathEnd *path_end2 = div2->pathEnd();
return PathEnd::cmp(path_end1, path_end2, sta_) > 0;
return PathEnd::cmp(path_end1, path_end2, true, sta_) > 0;
}
static void

View File

@ -50,7 +50,7 @@
namespace sta {
size_t PathGroup::group_path_count_max = std::numeric_limits<size_t>::max();
int PathGroup::group_path_count_max = std::numeric_limits<int>::max();
PathGroup *
PathGroup::makePathGroupSlack(const char *name,
@ -82,8 +82,8 @@ PathGroup::makePathGroupArrival(const char *name,
}
PathGroup::PathGroup(const char *name,
size_t group_path_count,
size_t endpoint_path_count,
int group_path_count,
int endpoint_path_count,
bool unique_pins,
bool unique_edges,
float slack_min,
@ -99,43 +99,36 @@ PathGroup::PathGroup(const char *name,
slack_min_(slack_min),
slack_max_(slack_max),
min_max_(min_max),
compare_slack_(cmp_slack),
threshold_(min_max->initValue()),
cmp_slack_(cmp_slack),
heap_(group_path_count, PathEndLess(cmp_slack, sta)),
sta_(sta)
{
}
PathGroup::~PathGroup()
PathEndSeq
PathGroup::pathEnds() const
{
deleteContents(path_ends_);
return heap_.contents();
}
bool
PathGroup::saveable(PathEnd *path_end)
{
float threshold;
{
LockGuard lock(lock_);
threshold = threshold_;
}
if (compare_slack_) {
if (cmp_slack_) {
// Crpr increases the slack, so check the slack
// without crpr first because it is expensive to find.
Slack slack = path_end->slackNoCrpr(sta_);
if (!delayIsInitValue(slack, min_max_)
&& delayLessEqual(slack, threshold, sta_)
&& delayLessEqual(slack, slack_max_, sta_)) {
// Now check with crpr.
slack = path_end->slack(sta_);
return delayLessEqual(slack, threshold, sta_)
&& delayLessEqual(slack, slack_max_, sta_)
return delayLessEqual(slack, slack_max_, sta_)
&& delayGreaterEqual(slack, slack_min_, sta_);
}
}
else {
const Arrival &arrival = path_end->dataArrivalTime(sta_);
return !delayIsInitValue(arrival, min_max_)
&& delayGreaterEqual(arrival, threshold, min_max_, sta_);
return !delayIsInitValue(arrival, min_max_);
}
return false;
}
@ -148,7 +141,7 @@ PathGroup::saveable(PathEnd *path_end)
bool
PathGroup::enumMinSlackUnderMin(PathEnd *path_end)
{
if (compare_slack_
if (cmp_slack_
&& endpoint_path_count_ > 1
&& slack_min_ > -INF) {
const Path *path = path_end->path();
@ -177,72 +170,28 @@ void
PathGroup::insert(PathEnd *path_end)
{
LockGuard lock(lock_);
path_ends_.push_back(path_end);
heap_.insert(path_end);
path_end->setPathGroup(this);
if (group_path_count_ != group_path_count_max
&& path_ends_.size() > group_path_count_ * 2)
prune();
}
void
PathGroup::prune()
{
sort();
VertexPathCountMap path_counts;
size_t end_count = 0;
for (unsigned i = 0; i < path_ends_.size(); i++) {
PathEnd *path_end = path_ends_[i];
Vertex *vertex = path_end->vertex(sta_);
// Squish up to endpoint_path_count path ends per vertex
// up to the front of path_ends_.
if (end_count < group_path_count_
&& path_counts[vertex] < endpoint_path_count_) {
path_ends_[end_count++] = path_end;
path_counts[vertex]++;
}
else
delete path_end;
}
path_ends_.resize(end_count);
// Set a threshold to the bottom of the sorted list that future
// inserts need to beat.
PathEnd *last_end = path_ends_[end_count - 1];
if (compare_slack_)
threshold_ = delayAsFloat(last_end->slack(sta_));
else
threshold_ = delayAsFloat(last_end->dataArrivalTime(sta_));
}
void
PathGroup::pushEnds(PathEndSeq &path_ends)
{
ensureSortedMaxPaths();
for (PathEnd *path_end : path_ends_)
path_ends.push_back(path_end);
}
void
PathGroup::ensureSortedMaxPaths()
{
if (path_ends_.size() > group_path_count_)
prune();
else
sort();
}
void
PathGroup::sort()
{
sta::sort(path_ends_, PathEndLess(sta_));
if (!heap_.empty()) {
PathEndSeq ends = heap_.extract();
path_ends.reserve(path_ends.size() + ends.size());
// Append heap path ends to path_ends.
path_ends.insert(path_ends.end(),
std::make_move_iterator(ends.begin()),
std::make_move_iterator(ends.end()));
}
}
void
PathGroup::clear()
{
LockGuard lock(lock_);
threshold_ = min_max_->initValue();
path_ends_.clear();
heap_.clear();
}
////////////////////////////////////////////////////////////////
@ -632,9 +581,8 @@ PathGroups::makePathEnds(ExceptionTo *to,
unique_pins_, unique_edges_, scenes, min_max);
pushEnds(path_ends);
if (sort_by_slack) {
sort(path_ends, PathEndLess(this));
}
if (sort_by_slack)
sort(path_ends, PathEndLess(true, this));
if (unconstrained_paths
&& path_ends.empty())
@ -663,12 +611,12 @@ private:
PathGroups *path_groups_;
PathGroupEndMap ends_;
PathEndLess cmp_;
PathEndLess less_;
};
MakePathEnds1::MakePathEnds1(PathGroups *path_groups) :
path_groups_(path_groups),
cmp_(path_groups)
less_(true, path_groups)
{
}
@ -693,7 +641,7 @@ MakePathEnds1::visitPathEnd(PathEnd *path_end,
// Only keep the path end with the smallest slack/latest arrival.
PathEnd *worst_end = findKey(ends_, group);
if (worst_end) {
if (cmp_(path_end, worst_end)) {
if (less_(path_end, worst_end)) {
ends_[group] = path_end->copy();
delete worst_end;
}
@ -741,8 +689,8 @@ private:
PathGroups *path_groups_;
const StaState *sta_;
PathGroupEndsMap ends_;
PathEndSlackLess slack_cmp_;
PathEndNoCrprLess path_no_crpr_cmp_;
PathEndSlackLess less_;
PathEndNoCrprLess path_no_crpr_less_;
};
MakePathEndsAll::MakePathEndsAll(int endpoint_path_count,
@ -750,8 +698,8 @@ MakePathEndsAll::MakePathEndsAll(int endpoint_path_count,
endpoint_path_count_(endpoint_path_count),
path_groups_(path_groups),
sta_(path_groups),
slack_cmp_(path_groups),
path_no_crpr_cmp_(path_groups)
less_(true, path_groups),
path_no_crpr_less_(path_groups)
{
}
@ -792,8 +740,8 @@ MakePathEndsAll::vertexEnd(Vertex *)
Debug *debug = sta_->debug();
for (auto [group, ends] : ends_) {
if (ends) {
sort(ends, slack_cmp_);
PathEndNoCrprSet unique_ends(path_no_crpr_cmp_);
sort(ends, less_);
PathEndNoCrprSet unique_ends(path_no_crpr_less_);
auto end_iter = ends->begin();
int n = 0;
while (end_iter != ends->end()
@ -898,11 +846,8 @@ PathGroups::enumPathEnds(PathGroup *group,
// enumerator.
PathEnum path_enum(group_path_count, endpoint_path_count,
unique_pins, unique_edges, cmp_slack, this);
for (PathEnd *end : group->pathEnds()) {
if (group->saveable(end)
|| group->enumMinSlackUnderMin(end))
path_enum.insert(end);
}
for (PathEnd *end : group->pathEnds())
path_enum.insert(end);
group->clear();
// Parallel path enumeratation to find the endpoint_path_count/max path ends.

View File

@ -497,8 +497,8 @@ Search::findPathEnds(ExceptionFrom *from,
bool unconstrained,
const SceneSeq &scenes,
const MinMaxAll *min_max,
size_t group_path_count,
size_t endpoint_path_count,
int group_path_count,
int endpoint_path_count,
bool unique_pins,
bool unique_edges,
float slack_min,