gpl: use cached pin count in reportInstanceExtensionByPinDensity - #11072
gpl: use cached pin count in reportInstanceExtensionByPinDensity#11072oharboe wants to merge 4 commits into
Conversation
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
There was a problem hiding this comment.
Code Review
This pull request optimizes the reportInstanceExtensionByPinDensity function in nesterovBase.cpp by utilizing the cached stats.pin_count instead of recalculating the pin count for each instance. The reviewer identified an existing logical issue where master statistics are overwritten in each iteration rather than accumulated, resulting in final reports that only reflect the last processed instance. The reviewer provided a suggestion to accumulate these areas and calculate the averages after the loop.
…tInstanceExtensionByPinDensity Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
|
/gemini review |
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
There was a problem hiding this comment.
Code Review
This pull request refactors the calculation of per-master derived metrics in reportInstanceExtensionByPinDensity to run post-loop, accumulating areas using +=. Feedback highlights a critical issue where master_stats_map is declared as static, causing accumulated statistics to persist and grow incorrectly across multiple calls. Additionally, there is a risk of signed integer overflow when multiplying pin_count and instance_count on large designs, which can be mitigated by casting to double.
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request refactors nesterovBase.cpp by removing the static qualifier from master_stats_map and moving the calculation of per-master derived metrics post-loop. Feedback is provided to optimize performance by accumulating raw DBU areas inside the loop and converting them to microns post-loop, which avoids repetitive floating-point divisions.
| stats.total_original_area += block->dbuAreaToMicrons(orig_area); | ||
| stats.total_extended_area += block->dbuAreaToMicrons(ext_area); | ||
| } | ||
|
|
||
| // Save area per pin | ||
| int pin_count = db_inst->getITerms().size(); | ||
| if (pin_count > 0) { | ||
| // Calculate per-master derived metrics post-loop | ||
| for (auto& entry : master_stats_map) { | ||
| MasterStats& stats = entry.second; | ||
| if (stats.pin_count > 0 && stats.instance_count > 0) { | ||
| stats.original_area_per_pin | ||
| = block->dbuAreaToMicrons(orig_area) / pin_count; | ||
| = stats.total_original_area | ||
| / (static_cast<double>(stats.pin_count) * stats.instance_count); | ||
| stats.extended_area_per_pin | ||
| = block->dbuAreaToMicrons(ext_area) / pin_count; | ||
| = stats.total_extended_area | ||
| / (static_cast<double>(stats.pin_count) * stats.instance_count); | ||
| } | ||
| // Populate area_diff as the percentage difference between extended and | ||
| // original area | ||
| if (orig_area != 0) { | ||
| if (stats.total_original_area != 0.0) { | ||
| stats.area_diff = 100.0f | ||
| * (static_cast<float>(ext_area - orig_area) | ||
| / static_cast<float>(orig_area)); | ||
| * (static_cast<float>(stats.total_extended_area | ||
| - stats.total_original_area) | ||
| / static_cast<float>(stats.total_original_area)); | ||
| } else { | ||
| stats.area_diff = 0.0f; | ||
| } |
There was a problem hiding this comment.
Performance Optimization
Currently, block->dbuAreaToMicrons() is called inside the loop for every single instance (potentially millions of times). Since dbuAreaToMicrons performs a floating-point division, doing this inside the loop adds unnecessary overhead.
We can optimize this by accumulating the raw DBU areas as integers/doubles inside the loop, and then converting the final accumulated totals to microns once per master in the post-loop. This avoids millions of division operations.
stats.total_original_area += orig_area;
stats.total_extended_area += ext_area;
}
// Calculate per-master derived metrics post-loop
for (auto& entry : master_stats_map) {
MasterStats& stats = entry.second;
stats.total_original_area = block->dbuAreaToMicrons(static_cast<int64_t>(stats.total_original_area));
stats.total_extended_area = block->dbuAreaToMicrons(static_cast<int64_t>(stats.total_extended_area));
if (stats.pin_count > 0 && stats.instance_count > 0) {
stats.original_area_per_pin
= stats.total_original_area
/ (static_cast<double>(stats.pin_count) * stats.instance_count);
stats.extended_area_per_pin
= stats.total_extended_area
/ (static_cast<double>(stats.pin_count) * stats.instance_count);
}
if (stats.total_original_area != 0.0) {
stats.area_diff = static_cast<float>(100.0 * (stats.total_extended_area - stats.total_original_area) / stats.total_original_area);
} else {
stats.area_diff = 0.0f;
}
Summary
Optimizes
gpl::NesterovBaseCommon::reportInstanceExtensionByPinDensity()to use the cachedstats.pin_countper cell master rather than queryingdb_inst->getITerms().size()redundantly for every instance across the design.Rationale
reportInstanceExtensionByPinDensity()collects per-master statistics and calculates area-per-pin metrics across all instances in the placement graph. Previously,db_inst->getITerms().size()was invoked on every instance. Sincedb_inst->getITerms().size()traverses an unindexed linked list of_dbITermpointers in OpenDB, calling it on large designs (>2M instances) spent 99.5% of reporting execution time inside linked-list traversals.Using the
stats.pin_countalready cached per cell master on line 1981 avoids 2.4 million linked-list traversals while producing identical statistics output.