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), comp_(comp),
min_heap_comp_(comp) min_heap_comp_(comp)
{ {
heap_.reserve(max_size);
} }
// Copy constructor // Copy constructor
@ -107,7 +106,12 @@ public:
setMaxSize(size_t max_size) setMaxSize(size_t max_size)
{ {
max_size_ = max_size; max_size_ = max_size;
heap_.reserve(max_size); }
void
reserve(size_t size)
{
heap_.reserve(size);
} }
// Insert an element into the heap. // Insert an element into the heap.
@ -172,8 +176,6 @@ public:
{ {
// Convert heap to sorted vector (best to worst) // Convert heap to sorted vector (best to worst)
std::sort_heap(heap_.begin(), heap_.end(), min_heap_comp_); 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_); std::vector<T> result = std::move(heap_);
heap_.clear(); heap_.clear();
return result; return result;
@ -181,11 +183,10 @@ public:
// Extract all elements sorted from best to worst (const version). // Extract all elements sorted from best to worst (const version).
// Creates a copy since we can't modify the heap. // 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::vector<T> temp_heap = heap_;
std::sort_heap(temp_heap.begin(), temp_heap.end(), min_heap_comp_); std::sort_heap(temp_heap.begin(), temp_heap.end(), min_heap_comp_);
std::reverse(temp_heap.begin(), temp_heap.end());
return temp_heap; return temp_heap;
} }
@ -245,7 +246,7 @@ private:
Compare comp_; Compare comp_;
explicit MinHeapCompare(const Compare& c) : comp_(c) {} explicit MinHeapCompare(const Compare& c) : comp_(c) {}
bool operator()(const T& a, const T& b) const { 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, static bool less(const PathEnd *path_end1,
const PathEnd *path_end2, const PathEnd *path_end2,
// Compare slack (if constrained), or arrival when false.
bool cmp_slack,
const StaState *sta); const StaState *sta);
static int cmp(const PathEnd *path_end1, static int cmp(const PathEnd *path_end1,
const PathEnd *path_end2, const PathEnd *path_end2,
// Compare slack (if constrained), or arrival when false.
bool cmp_slack,
const StaState *sta); const StaState *sta);
static int cmpSlack(const PathEnd *path_end1, static int cmpSlack(const PathEnd *path_end1,
const PathEnd *path_end2, const PathEnd *path_end2,
@ -611,11 +615,13 @@ protected:
class PathEndLess class PathEndLess
{ {
public: public:
PathEndLess(const StaState *sta); PathEndLess(bool cmp_slack,
const StaState *sta);
bool operator()(const PathEnd *path_end1, bool operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const; const PathEnd *path_end2) const;
protected: protected:
bool cmp_slack_;
const StaState *sta_; const StaState *sta_;
}; };
@ -623,11 +629,13 @@ protected:
class PathEndSlackLess class PathEndSlackLess
{ {
public: public:
PathEndSlackLess(const StaState *sta); PathEndSlackLess(bool cmp_slack,
const StaState *sta);
bool operator()(const PathEnd *path_end1, bool operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const; const PathEnd *path_end2) const;
protected: protected:
bool cmp_slack_;
const StaState *sta_; const StaState *sta_;
}; };

View File

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

View File

@ -100,8 +100,8 @@ public:
bool unconstrained, bool unconstrained,
const SceneSeq &scenes, const SceneSeq &scenes,
const MinMaxAll *min_max, const MinMaxAll *min_max,
size_t group_path_count, int group_path_count,
size_t endpoint_path_count, int endpoint_path_count,
bool unique_pins, bool unique_pins,
bool unique_edges, bool unique_edges,
float slack_min, 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 bool
PathEnd::less(const PathEnd *path_end1, PathEnd::less(const PathEnd *path_end1,
const PathEnd *path_end2, const PathEnd *path_end2,
bool cmp_slack,
const StaState *sta) const StaState *sta)
{ {
return cmp(path_end1, path_end2, sta) < 0; return cmp(path_end1, path_end2, cmp_slack, sta) < 0;
} }
int int
PathEnd::cmp(const PathEnd *path_end1, PathEnd::cmp(const PathEnd *path_end1,
const PathEnd *path_end2, const PathEnd *path_end2,
bool cmp_slack,
const StaState *sta) const StaState *sta)
{ {
int cmp = path_end1->isUnconstrained() int cmp = !cmp_slack || path_end1->isUnconstrained()
? -cmpArrival(path_end1, path_end2, sta) ? -cmpArrival(path_end1, path_end2, sta)
: cmpSlack(path_end1, path_end2, sta); : cmpSlack(path_end1, path_end2, sta);
if (cmp == 0) { 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) sta_(sta)
{ {
} }

View File

@ -91,7 +91,7 @@ DiversionGreater::operator()(Diversion *div1,
{ {
PathEnd *path_end1 = div1->pathEnd(); PathEnd *path_end1 = div1->pathEnd();
PathEnd *path_end2 = div2->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 static void

View File

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

View File

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