Skip to content

Commit fa7e727

Browse files
[AP][GlobalPlacement] Improved Partial Legalizer Legality
Updated the partial legalizer to now take into account block types when spreading blocks. This will create windows around overfilled bins that is aware of which block types are overfilled and how large the window needs to be to accomodate them. It also takes these block types into account when spreading to only allow blocks to spread into sub-windows that they can exist in. This improves quality but was detremental to performance, so some performance improvements were needed. To improve the performance of the partial legalizer, I split the problem into groups of models which must be spread together. This allows us to create tighter windows and can make some parts of the legalizer more efficient. Create a model grouper class which forms the model pack patterns into a graph and find disconnected sub-graphs to form the model groups. Also improved the window generation by pre-clustering the overfilled bins before creating the windows. This sped up the window generation code since less windows overlap.
1 parent ccb2396 commit fa7e727

18 files changed

+1487
-275
lines changed

vpr/src/analytical_place/analytical_solver.cpp

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -236,41 +236,17 @@ void QPHybridSolver::init_linear_system() {
236236
A_sparse.setFromTriplets(tripletList.begin(), tripletList.end());
237237
}
238238

239-
/**
240-
* @brief Helper method to update the linear system with anchors to the current
241-
* partial placement.
242-
*
243-
* For each moveable block (with row = i) in the netlist:
244-
* A[i][i] = A[i][i] + coeff_pseudo_anchor;
245-
* b[i] = b[i] + pos[block(i)] * coeff_pseudo_anchor;
246-
* Where coeff_pseudo_anchor grows with each iteration.
247-
*
248-
* This is basically a fast way of adding a connection between all moveable
249-
* blocks in the netlist and their target fixed placement location.
250-
*
251-
* See add_connection_to_system.
252-
*
253-
* @param A_sparse_diff The ceofficient matrix to update.
254-
* @param b_x_diff The x-dimension constant vector to update.
255-
* @param b_y_diff The y-dimension constant vector to update.
256-
* @param p_placement The location the moveable blocks should be anchored
257-
* to.
258-
* @param num_moveable_blocks The number of moveable blocks in the netlist.
259-
* @param row_id_to_blk_id Lookup for the row id from the APBlock Id.
260-
* @param iteration The current iteration of the Global Placer.
261-
*/
262-
static inline void update_linear_system_with_anchors(Eigen::SparseMatrix<double>& A_sparse_diff,
263-
Eigen::VectorXd& b_x_diff,
264-
Eigen::VectorXd& b_y_diff,
265-
PartialPlacement& p_placement,
266-
size_t num_moveable_blocks,
267-
vtr::vector<APRowId, APBlockId> row_id_to_blk_id,
268-
unsigned iteration) {
239+
void QPHybridSolver::update_linear_system_with_anchors(
240+
Eigen::SparseMatrix<double>& A_sparse_diff,
241+
Eigen::VectorXd& b_x_diff,
242+
Eigen::VectorXd& b_y_diff,
243+
PartialPlacement& p_placement,
244+
unsigned iteration) {
269245
// Anchor weights grow exponentially with iteration.
270-
double coeff_pseudo_anchor = 0.01 * std::exp((double)iteration / 5);
271-
for (size_t row_id_idx = 0; row_id_idx < num_moveable_blocks; row_id_idx++) {
246+
double coeff_pseudo_anchor = anchor_weight_mult_ * std::exp((double)iteration / anchor_weight_exp_fac_);
247+
for (size_t row_id_idx = 0; row_id_idx < num_moveable_blocks_; row_id_idx++) {
272248
APRowId row_id = APRowId(row_id_idx);
273-
APBlockId blk_id = row_id_to_blk_id[row_id];
249+
APBlockId blk_id = row_id_to_blk_id_[row_id];
274250
double pseudo_w = coeff_pseudo_anchor;
275251
A_sparse_diff.coeffRef(row_id_idx, row_id_idx) += pseudo_w;
276252
b_x_diff(row_id_idx) += pseudo_w * p_placement.block_x_locs[blk_id];
@@ -289,8 +265,7 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
289265
// anchor-points (fixed block positions).
290266
if (iteration != 0) {
291267
update_linear_system_with_anchors(A_sparse_diff, b_x_diff, b_y_diff,
292-
p_placement, num_moveable_blocks_,
293-
row_id_to_blk_id_, iteration);
268+
p_placement, iteration);
294269
}
295270
// Verify that the constant vectors are valid.
296271
VTR_ASSERT_DEBUG(!b_x_diff.hasNaN() && "b_x has NaN!");

vpr/src/analytical_place/analytical_solver.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,21 @@ class QPHybridSolver : public AnalyticalSolver {
155155
/// sparse.
156156
static constexpr size_t star_num_pins_threshold = 3;
157157

158+
// The following constants are used to configure the anchor weighting.
159+
// The weights of anchors grow exponentially each iteration by the following
160+
// function:
161+
// anchor_w = anchor_weight_mult_ * e^(iter / anchor_weight_exp_fac_)
162+
// The numbers below were empircally found to work well.
163+
164+
/// @brief Multiplier for the anchorweight. The smaller this number is, the
165+
/// weaker the anchors will be at the start.
166+
static constexpr double anchor_weight_mult_ = 0.001;
167+
168+
/// @brief Factor for controlling the growth of the exponential term in the
169+
/// weight factor function. Larger numbers will cause the anchor
170+
/// weights to grow slower.
171+
static constexpr double anchor_weight_exp_fac_ = 5.0;
172+
158173
/**
159174
* @brief Initializes the linear system of Ax = b_x and Ay = b_y based on
160175
* the APNetlist and the fixed APBlock locations.
@@ -165,6 +180,35 @@ class QPHybridSolver : public AnalyticalSolver {
165180
*/
166181
void init_linear_system();
167182

183+
/**
184+
* @brief Helper method to update the linear system with anchors to the
185+
* current partial placement.
186+
*
187+
* For each moveable block (with row = i) in the netlist:
188+
* A[i][i] = A[i][i] + coeff_pseudo_anchor;
189+
* b[i] = b[i] + pos[block(i)] * coeff_pseudo_anchor;
190+
* Where coeff_pseudo_anchor grows with each iteration.
191+
*
192+
* This is basically a fast way of adding a connection between all moveable
193+
* blocks in the netlist and their target fixed placement location.
194+
*
195+
* See add_connection_to_system.
196+
*
197+
* @param A_sparse_diff The ceofficient matrix to update.
198+
* @param b_x_diff The x-dimension constant vector to update.
199+
* @param b_y_diff The y-dimension constant vector to update.
200+
* @param p_placement The location the moveable blocks should be
201+
* anchored to.
202+
* @param num_moveable_blocks The number of moveable blocks in the netlist.
203+
* @param row_id_to_blk_id Lookup for the row id from the APBlock Id.
204+
* @param iteration The current iteration of the Global Placer.
205+
*/
206+
void update_linear_system_with_anchors(Eigen::SparseMatrix<double>& A_sparse_diff,
207+
Eigen::VectorXd& b_x_diff,
208+
Eigen::VectorXd& b_y_diff,
209+
PartialPlacement& p_placement,
210+
unsigned iteration);
211+
168212
// The following variables represent the linear system without any anchor
169213
// points. These are filled in the constructor and never modified.
170214
// When the anchor-points are taken into consideration, the diagonal of the

vpr/src/analytical_place/flat_placement_bins.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ class FlatPlacementBins {
111111
inline const vtr::Rect<double>& bin_region(FlatPlacementBinId bin_id) const {
112112
VTR_ASSERT(bin_id.is_valid());
113113
return bin_region_[bin_id];
114-
;
115114
}
116115

117116
/**

vpr/src/analytical_place/flat_placement_density_manager.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ FlatPlacementDensityManager::FlatPlacementDensityManager(const APNetlist& ap_net
8080
auto tile_type = device_grid.get_physical_type(tile_loc);
8181
int tw = tile_type->width;
8282
int th = tile_type->height;
83+
VTR_ASSERT_SAFE(tw != 0 && th != 0);
8384
vtr::Rect<double> new_bin_region(vtr::Point<double>(x, y),
8485
vtr::Point<double>(x + tw,
8586
y + th));
@@ -162,6 +163,10 @@ void FlatPlacementDensityManager::remove_block_from_bin(APBlockId blk_id,
162163
}
163164

164165
void FlatPlacementDensityManager::import_placement_into_bins(const PartialPlacement& p_placement) {
166+
// Empty the bins such that all blocks are no longer within the bins.
167+
empty_bins();
168+
169+
// Insert each block in the netlist into their bin based on their placement.
165170
// TODO: Maybe import the fixed block locations in the constructor and then
166171
// only import the moveable block locations.
167172
for (APBlockId blk_id : ap_netlist_.blocks()) {
@@ -215,9 +220,9 @@ void FlatPlacementDensityManager::empty_bins() {
215220
// Reset all of the bins and their utilizations.
216221
for (FlatPlacementBinId bin_id : bins_.bins()) {
217222
bins_.remove_all_blocks_from_bin(bin_id);
218-
bin_utilization_[bin_id] = PrimitiveVector();
219-
bin_overfill_[bin_id] = calc_bin_overfill(bin_utilization_[bin_id], bin_capacity_[bin_id]);
220-
bin_underfill_[bin_id] = calc_bin_underfill(bin_utilization_[bin_id], bin_capacity_[bin_id]);
223+
bin_utilization_[bin_id].clear();
224+
bin_overfill_[bin_id].clear();
225+
bin_underfill_[bin_id] = bin_capacity_[bin_id];
221226
}
222227
// Once all the bins are reset, all bins should be empty; therefore no bins
223228
// are overfilled.

vpr/src/analytical_place/flat_placement_density_manager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ class FlatPlacementDensityManager {
185185
* @brief Import the given flat placement into the bins.
186186
*
187187
* This will place AP blocks into the bins that they are placed over.
188+
*
189+
* This will reset the bins before importing the placement. Anything inside
190+
* the bins will be removed.
188191
*/
189192
void import_placement_into_bins(const PartialPlacement& p_placement);
190193

vpr/src/analytical_place/flat_placement_mass_calculator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ static void print_capacities(const std::vector<PrimitiveVector>& logical_block_t
234234
VTR_LOG("\n");
235235
}
236236
VTR_LOG("\n");
237+
// TODO: Print the masses of each model.
237238
}
238239

239240
FlatPlacementMassCalculator::FlatPlacementMassCalculator(const APNetlist& ap_netlist,

vpr/src/analytical_place/global_placer.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@
1313
#include "analytical_solver.h"
1414
#include "ap_flow_enums.h"
1515
#include "ap_netlist.h"
16+
#include "ap_netlist_fwd.h"
1617
#include "atom_netlist.h"
1718
#include "device_grid.h"
19+
#include "flat_placement_bins.h"
1820
#include "flat_placement_density_manager.h"
21+
#include "globals.h"
1922
#include "partial_legalizer.h"
2023
#include "partial_placement.h"
2124
#include "physical_types.h"
25+
#include "primitive_vector.h"
2226
#include "vpr_error.h"
2327
#include "vtr_log.h"
2428
#include "vtr_time.h"
@@ -90,9 +94,74 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_partial_legalizer partial_legalizer_type,
9094
partial_legalizer_ = make_partial_legalizer(partial_legalizer_type,
9195
ap_netlist_,
9296
density_manager_,
97+
prepacker,
9398
log_verbosity_);
9499
}
95100

101+
/**
102+
* @brief Helper method to print the statistics on the given partial placement.
103+
*/
104+
static void print_placement_stats(const PartialPlacement& p_placement,
105+
const APNetlist& ap_netlist,
106+
FlatPlacementDensityManager& density_manager) {
107+
// Print the placement HPWL
108+
VTR_LOG("\tPlacement HPWL: %f\n", p_placement.get_hpwl(ap_netlist));
109+
110+
// Print density information. Need to reset the density manager to ensure
111+
// the data is valid.
112+
density_manager.import_placement_into_bins(p_placement);
113+
114+
// Print the number of overfilled bins.
115+
size_t num_overfilled_bins = density_manager.get_overfilled_bins().size();
116+
VTR_LOG("\tNumber of overfilled bins: %zu\n", num_overfilled_bins);
117+
118+
// Print the average overfill
119+
float total_overfill = 0.0f;
120+
for (FlatPlacementBinId bin_id : density_manager.get_overfilled_bins()) {
121+
total_overfill += density_manager.get_bin_overfill(bin_id).manhattan_norm();
122+
}
123+
float avg_overfill = 0.0f;
124+
if (num_overfilled_bins != 0)
125+
avg_overfill = total_overfill / static_cast<float>(num_overfilled_bins);
126+
VTR_LOG("\tAverage overfill magnitude: %f\n", avg_overfill);
127+
128+
// Print the number of overfilled tiles per type.
129+
const auto& physical_tile_types = g_vpr_ctx.device().physical_tile_types;
130+
const auto& device_grid = g_vpr_ctx.device().grid;
131+
std::vector<unsigned> overfilled_tiles_by_type(physical_tile_types.size(), 0);
132+
for (FlatPlacementBinId bin_id : density_manager.get_overfilled_bins()) {
133+
const auto& bin_region = density_manager.flat_placement_bins().bin_region(bin_id);
134+
auto tile_loc = t_physical_tile_loc((int)bin_region.xmin(),
135+
(int)bin_region.ymin(),
136+
0);
137+
auto tile_type = device_grid.get_physical_type(tile_loc);
138+
overfilled_tiles_by_type[tile_type->index]++;
139+
}
140+
VTR_LOG("\tOverfilled bins by tile type:\n");
141+
for (size_t type_idx = 0; type_idx < physical_tile_types.size(); type_idx++) {
142+
VTR_LOG("\t\t%10s: %zu\n",
143+
physical_tile_types[type_idx].name.c_str(),
144+
overfilled_tiles_by_type[type_idx]);
145+
}
146+
147+
// Count the number of blocks that were placed in a bin which they cannot
148+
// physically be placed into (according to their mass).
149+
unsigned num_misplaced_blocks = 0;
150+
for (FlatPlacementBinId bin_id : density_manager.get_overfilled_bins()) {
151+
for (APBlockId ap_blk_id : density_manager.flat_placement_bins().bin_contained_blocks(bin_id)) {
152+
// Get the blk mass and project it onto the capacity of its bin.
153+
PrimitiveVector blk_mass = density_manager.mass_calculator().get_block_mass(ap_blk_id);
154+
PrimitiveVector projected_mass = blk_mass;
155+
projected_mass.project(density_manager.get_bin_capacity(bin_id));
156+
// If the projected mass does not match its match, this implies that
157+
// there this block does not belong in this bin.
158+
if (projected_mass != blk_mass)
159+
num_misplaced_blocks++;
160+
}
161+
}
162+
VTR_LOG("\tNumber of blocks in an incompatible bin: %zu\n", num_misplaced_blocks);
163+
}
164+
96165
/**
97166
* @brief Helper method to print the header of the per-iteration status updates
98167
* of the global placer.
@@ -177,6 +246,13 @@ PartialPlacement SimPLGlobalPlacer::place() {
177246
if (hpwl_relative_gap < target_hpwl_relative_gap_)
178247
break;
179248
}
249+
250+
// Print some statistics on the final placement.
251+
VTR_LOG("Placement after Global Placement:\n");
252+
print_placement_stats(p_placement,
253+
ap_netlist_,
254+
*density_manager_);
255+
180256
// Return the placement from the final iteration.
181257
// TODO: investigate saving the best solution found so far. It should be
182258
// cheap to save a copy of the PartialPlacement object.

vpr/src/analytical_place/global_placer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ class SimPLGlobalPlacer : public GlobalPlacer {
116116
/// lower-bound placements. The placer will stop if the difference
117117
/// between the two bounds, normalized to the upper-bound, is smaller
118118
/// than this number.
119-
static constexpr double target_hpwl_relative_gap_ = 0.10;
119+
/// This number was empircally found to work well.
120+
static constexpr double target_hpwl_relative_gap_ = 0.05;
120121

121122
/// @brief The solver which generates the lower-bound placement.
122123
std::unique_ptr<AnalyticalSolver> solver_;

0 commit comments

Comments
 (0)