Skip to content

Commit 47806e4

Browse files
[AP][APPack] Density Estimation Upstream
This was code written for a TODAES publication which found that APPack struggled when the device was aggressively sized. This PR brings in improvments to APPack which allow it to estimate how much space is available on the device and use that information to select different parameters to more aggressively pack. For example, if it detects that the device is limited in CLBs, it will increase the max displacement threshold for CLBs proportional to how limited the CLBs are. If the limitation is extreme, it automatically turns on unrelated clustering early. This should help the auto-selected device sizes be more aligned with the traditional flow as well.
1 parent f9d73eb commit 47806e4

10 files changed

Lines changed: 234 additions & 51 deletions

vpr/src/base/vpr_api.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,8 @@ bool vpr_pack(t_vpr_setup& vpr_setup, const t_arch& arch) {
757757
// that downstream stages (e.g. RAM mapper) can query realistic device
758758
// dimensions before packing. The packer may later grow or shrink the device
759759
// size to match the actual resource requirements after packing completes.
760-
DeviceSizeEstimator device_size_estimator(vpr_setup, arch, prepacker);
760+
DeviceSizeEstimator device_size_estimator(vpr_setup, arch, prepacker,
761+
/*always_estimate_resource_requirement=*/g_vpr_ctx.atom().flat_placement_info().valid);
761762

762763
// Infer logical RAMs and assign to physical types to prioritize during packing.
763764
// For the auto-device flow, reuse the groups already computed by the estimator.
@@ -778,7 +779,8 @@ bool vpr_pack(t_vpr_setup& vpr_setup, const t_arch& arch) {
778779
pre_cluster_timing_manager,
779780
g_vpr_ctx.atom().flat_placement_info(),
780781
vpr_setup,
781-
ram_mapper);
782+
ram_mapper,
783+
device_size_estimator.estimated_type_instance_counts());
782784
}
783785

784786
void vpr_load_packing(const t_vpr_setup& vpr_setup, const t_arch& arch) {

vpr/src/pack/appack_context.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ struct APPackContext : public Context {
104104
device_grid);
105105

106106
unrelated_clustering_manager.init(ap_opts.appack_unrelated_clustering_args,
107-
logical_block_types);
107+
logical_block_types,
108+
device_grid);
108109
}
109110
}
110111

vpr/src/pack/appack_max_dist_th_manager.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ void APPackMaxDistThManager::init(const std::vector<std::string>& max_dist_ths,
2424
const DeviceGrid& device_grid) {
2525
// Compute the max device distance based on the width and height of the
2626
// device. This is the L1 (manhattan) distance.
27-
max_distance_on_device_ = device_grid.width() + device_grid.height();
27+
float layer_span_contr = device_grid.get_num_layers() > 1 ? device_grid.get_num_layers() : 0;
28+
max_distance_on_device_ = device_grid.width() + device_grid.height() + layer_span_contr;
2829

2930
// Automatically set the max distance thresholds.
3031
auto_set_max_distance_thresholds(logical_block_types, device_grid);

vpr/src/pack/appack_max_dist_th_manager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class APPackMaxDistThManager {
4545
// Logic blocks (such as CLBs and LABs) tend to have more resources on the
4646
// device, thus they have tighter thresholds. This was found to work well.
4747
static constexpr float logic_block_max_dist_th_scale_ = 0.02f;
48-
static constexpr float logic_block_max_dist_th_offset_ = 10.0f;
48+
static constexpr float logic_block_max_dist_th_offset_ = 5.0f;
4949

5050
// Memory blocks (i.e. blocks that contain pb_types of the memory class)
5151
// seem to have very touchy packing; thus these do not have the max

vpr/src/pack/appack_unrelated_clustering_manager.cpp

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,17 @@
88
#include "appack_unrelated_clustering_manager.h"
99
#include "ap_argparse_utils.h"
1010
#include "arch_util.h"
11+
#include "device_grid.h"
1112
#include "vpr_error.h"
13+
#include "vpr_utils.h"
1214

1315
void APPackUnrelatedClusteringManager::init(
1416
const std::vector<std::string>& unrelated_clustering_args,
15-
const std::vector<t_logical_block_type>& logical_block_types) {
17+
const std::vector<t_logical_block_type>& logical_block_types,
18+
const DeviceGrid& device_grid) {
1619

17-
// Set the max unrelated tile distances for all logical block types.
18-
// By default, we set this to a low value to only allow unrelated molecules
19-
// that are very close to the cluster being created.
20-
// NOTE: Molecules within the same tile as the centroid are considered to have
21-
// 0 distance. The distance is computed relative to the bounds of the
22-
// tile containing the centroid.
23-
max_unrelated_tile_distance_.resize(logical_block_types.size(),
24-
default_max_unrelated_tile_distance_);
25-
max_unrelated_clustering_attempts_.resize(logical_block_types.size(),
26-
default_max_unrelated_clustering_attempts_);
27-
28-
// For memories (such as BRAMs and MLABs), we do not perform unrelated clustering.
29-
for (const t_logical_block_type& lb_ty : logical_block_types) {
30-
// Skip the empty logical block type. This should not have any blocks.
31-
if (lb_ty.is_empty())
32-
continue;
33-
34-
// If the logical block type contains a pb_type which is a memory class,
35-
// it is assumed to be a BRAM or an MLAB.
36-
bool has_memory = pb_type_contains_memory_pbs(lb_ty.pb_type);
37-
if (!has_memory)
38-
continue;
39-
40-
// Do not do unrelated clustering on memories. It is not worth it.
41-
max_unrelated_clustering_attempts_[lb_ty.index] = 0;
42-
}
20+
// Automatically set the unrelated clustering parameters.
21+
auto_set_unrelated_clustering_params(logical_block_types, device_grid);
4322

4423
// At this point, the data structures have been initialized properly.
4524
is_initialized_ = true;
@@ -81,3 +60,45 @@ void APPackUnrelatedClusteringManager::init(
8160
max_unrelated_clustering_attempts_[lb_ty_index] = logical_block_max_unrel_attempts;
8261
}
8362
}
63+
64+
void APPackUnrelatedClusteringManager::auto_set_unrelated_clustering_params(
65+
const std::vector<t_logical_block_type>& logical_block_types,
66+
const DeviceGrid& device_grid) {
67+
68+
// Set the max unrelated tile distances for all logical block types.
69+
// By default, we set this to a low value to only allow unrelated molecules
70+
// that are very close to the cluster being created.
71+
// NOTE: Molecules within the same tile as the centroid are considered to have
72+
// 0 distance. The distance is computed relative to the bounds of the
73+
// tile containing the centroid.
74+
max_unrelated_tile_distance_.resize(logical_block_types.size(),
75+
default_max_unrelated_tile_distance_);
76+
max_unrelated_clustering_attempts_.resize(logical_block_types.size(),
77+
default_max_unrelated_clustering_attempts_);
78+
79+
// Find which (if any) of the logical block types most looks like a CLB block.
80+
t_logical_block_type_ptr logic_block_type = infer_logic_block_type(device_grid);
81+
82+
for (const t_logical_block_type& lb_ty : logical_block_types) {
83+
// Skip the empty logical block type. This should not have any blocks.
84+
if (lb_ty.is_empty())
85+
continue;
86+
87+
// If the logical block type contains a pb_type which is a memory class,
88+
// it is assumed to be a BRAM or an MLAB.
89+
bool has_memory = pb_type_contains_memory_pbs(lb_ty.pb_type);
90+
if (!has_memory)
91+
continue;
92+
93+
// Do not do unrelated clustering on memories. It is not worth it.
94+
max_unrelated_clustering_attempts_[lb_ty.index] = 0;
95+
96+
// Logic blocks (such as CLBs and LABs) were found to benefit from
97+
// different unrelated clustering parameters than the default.
98+
bool is_logic_block_type = (lb_ty.index == logic_block_type->index);
99+
if (is_logic_block_type) {
100+
max_unrelated_tile_distance_[lb_ty.index] = logic_block_max_unrelated_tile_distance_;
101+
max_unrelated_clustering_attempts_[lb_ty.index] = logic_block_max_unrelated_clustering_attempts_;
102+
}
103+
}
104+
}

vpr/src/pack/appack_unrelated_clustering_manager.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include "physical_types.h"
1313
#include "vtr_assert.h"
1414

15+
// Forward declarations.
16+
class DeviceGrid;
17+
1518
/**
1619
* @brief Manager class for unrelated clustering in APPack.
1720
*
@@ -55,6 +58,12 @@ class APPackUnrelatedClusteringManager {
5558
*/
5659
static constexpr int default_max_unrelated_clustering_attempts_ = 1;
5760

61+
// Logic blocks (such as CLBs and LABs) were found to benefit from a larger
62+
// search distance and number of attempts than the default. These numbers
63+
// were found empirically to work well.
64+
static constexpr float logic_block_max_unrelated_tile_distance_ = 4.0f;
65+
static constexpr int logic_block_max_unrelated_clustering_attempts_ = 1;
66+
5867
public:
5968
/**
6069
* @brief When the packing is particularly difficult, and it has failed enough
@@ -77,9 +86,11 @@ class APPackUnrelatedClusteringManager {
7786
* behavior.
7887
* @param logical_block_types
7988
* A vector of all logical block types in the architecture.
89+
* @param device_grid
8090
*/
8191
void init(const std::vector<std::string>& unrelated_clustering_args,
82-
const std::vector<t_logical_block_type>& logical_block_types);
92+
const std::vector<t_logical_block_type>& logical_block_types,
93+
const DeviceGrid& device_grid);
8394

8495
/**
8596
* @brief Get the max search distance for the given logical block type.
@@ -137,6 +148,14 @@ class APPackUnrelatedClusteringManager {
137148
}
138149

139150
private:
151+
/**
152+
* @brief Helper method that initializes the unrelated clustering parameters
153+
* of all logical block types to reasonable numbers based on the
154+
* characteristics of the logical block type.
155+
*/
156+
void auto_set_unrelated_clustering_params(const std::vector<t_logical_block_type>& logical_block_types,
157+
const DeviceGrid& device_grid);
158+
140159
/// @brief A flag which shows if the data within this class has been initialized
141160
/// or not.
142161
bool is_initialized_ = false;

vpr/src/pack/device_size_estimate.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,16 @@ static bool primitive_has_external_output(const t_pb_graph_node* prim) {
252252
return false;
253253
}
254254

255-
std::map<t_logical_block_type_ptr, size_t> DeviceSizeEstimator::estimate_resource_requirement(const Prepacker& prepacker) {
255+
std::map<t_logical_block_type_ptr, size_t> DeviceSizeEstimator::estimate_resource_requirement(const Prepacker& prepacker, bool store_ram_groups) {
256256
vtr::ScopedStartFinishTimer timer("Estimate Resource Requirement");
257257
const auto& atom_ctx = g_vpr_ctx.atom();
258258

259259
// Group RAM atoms and assign to minimum-area types.
260-
// Results are stored in ram_groups_ for later use by RamMapper.
261-
ram_groups_ = group_ram_atoms(atom_ctx.netlist(), prepacker);
262-
assign_ram_groups_by_min_area(ram_groups_, false /*is_fixed_device*/);
260+
// If requested, results are stored in ram_groups_ for later use by RamMapper.
261+
vtr::vector<LogicalRamGroupId, LogicalRamGroup> ram_groups = group_ram_atoms(atom_ctx.netlist(), prepacker);
262+
assign_ram_groups_by_min_area(ram_groups, false /*is_fixed_device*/);
263+
if (store_ram_groups)
264+
ram_groups_ = ram_groups;
263265

264266
// Group non-RAM molecules by their logical block type.
265267
vtr::vector<LogicalModelId, std::vector<t_logical_block_type_ptr>>
@@ -306,8 +308,8 @@ std::map<t_logical_block_type_ptr, size_t> DeviceSizeEstimator::estimate_resourc
306308
}
307309

308310
// Estimate instance counts for RAM types using logical RAM groups.
309-
for (LogicalRamGroupId ram_group_id : ram_groups_.keys()) {
310-
const LogicalRamGroup& ram_group = ram_groups_[ram_group_id];
311+
for (LogicalRamGroupId ram_group_id : ram_groups.keys()) {
312+
const LogicalRamGroup& ram_group = ram_groups[ram_group_id];
311313
t_logical_block_type_ptr logical_type = ram_group.pre_assigned_type ? ram_group.pre_assigned_type : ram_group.candidate_types[0];
312314
size_t inferred_number_of_instances = std::ceil(vtr::safe_ratio<float>(ram_group.total_memory_slices, ram_group.candidate_capacity.at(logical_type)));
313315
num_type_instances[logical_type] += inferred_number_of_instances;
@@ -378,7 +380,8 @@ static std::pair<size_t, size_t> read_rr_graph_grid_dims(const std::string& file
378380

379381
DeviceSizeEstimator::DeviceSizeEstimator(t_vpr_setup& vpr_setup,
380382
const t_arch& arch,
381-
const Prepacker& prepacker) {
383+
const Prepacker& prepacker,
384+
bool always_estimate_resource_requirement) {
382385
vtr::ScopedStartFinishTimer timer("Estimate Device Size");
383386
const std::string& device_layout = vpr_setup.PackerOpts.device_layout;
384387
const t_packer_opts& packer_opts = vpr_setup.PackerOpts;
@@ -416,6 +419,8 @@ DeviceSizeEstimator::DeviceSizeEstimator(t_vpr_setup& vpr_setup,
416419
// resizing during and after packing.
417420
device_ctx.grid = create_device_grid(device_layout, arch.grid_layouts, width, height);
418421
device_ctx.grid.set_fixed_by_rr_graph(true);
422+
if (always_estimate_resource_requirement)
423+
estimated_num_type_instances_ = estimate_resource_requirement(prepacker, /*store_ram_groups=*/false);
419424
return;
420425
}
421426

@@ -426,6 +431,8 @@ DeviceSizeEstimator::DeviceSizeEstimator(t_vpr_setup& vpr_setup,
426431
device_ctx.grid = create_device_grid(device_layout, arch.grid_layouts,
427432
num_type_instances,
428433
packer_opts.target_device_utilization);
434+
if (always_estimate_resource_requirement)
435+
estimated_num_type_instances_ = estimate_resource_requirement(prepacker, /*store_ram_groups=*/false);
429436
return;
430437
}
431438

@@ -437,12 +444,15 @@ DeviceSizeEstimator::DeviceSizeEstimator(t_vpr_setup& vpr_setup,
437444
{}, packer_opts.target_device_utilization,
438445
vpr_setup.device_width);
439446
report_device_grid_stats(device_ctx.grid);
447+
if (always_estimate_resource_requirement)
448+
estimated_num_type_instances_ = estimate_resource_requirement(prepacker, /*store_ram_groups=*/false);
440449
return;
441450
}
442451

443452
VTR_LOG("Device layout '%s' selected. Need to estimate device size.\n", device_layout.c_str());
444453

445454
std::map<t_logical_block_type_ptr, size_t> num_type_instances = estimate_resource_requirement(prepacker);
455+
estimated_num_type_instances_ = num_type_instances;
446456
VTR_LOG("Estimated resource requirements:\n");
447457
for (auto& [type_ptr, count] : num_type_instances)
448458
VTR_LOG(" %s: %zu requested instances\n", type_ptr->name.c_str(), count);

vpr/src/pack/device_size_estimate.h

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,65 @@ struct t_vpr_setup;
3232
*/
3333
class DeviceSizeEstimator {
3434
public:
35+
/**
36+
* @param always_estimate_resource_requirement
37+
* If true, the resource requirement estimate (see
38+
* estimated_type_instance_counts()) is computed even when the
39+
* device size itself does not need estimating (fixed device layout,
40+
* RR graph provided, or auto layout with a fixed width). Defaults to
41+
* false since this estimate involves a mini-packing simulation, and
42+
* most callers only need the device grid sized, not the raw
43+
* per-type estimate.
44+
*/
3545
DeviceSizeEstimator(t_vpr_setup& vpr_setup,
3646
const t_arch& arch,
37-
const Prepacker& prepacker);
47+
const Prepacker& prepacker,
48+
bool always_estimate_resource_requirement = false);
3849

3950
/// @brief Returns the RAM groups computed during estimation.
4051
/// Empty if the device layout was fixed (no estimation needed).
4152
const vtr::vector<LogicalRamGroupId, LogicalRamGroup>& ram_groups() const {
4253
return ram_groups_;
4354
}
4455

56+
/// @brief Returns the estimated number of instances required for each
57+
/// logical block type, computed during device size estimation.
58+
/// Empty unless estimation was performed (device layout was
59+
/// "auto" with no fixed width, or always_estimate_resource_requirement
60+
/// was set to true in the constructor).
61+
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts() const {
62+
return estimated_num_type_instances_;
63+
}
64+
4565
private:
4666
/**
4767
* @brief Estimates the number of instances required for each logical block
4868
* type to fit the design.
4969
*
50-
* Groups RAM atoms, assigns them to minimum-area types (stored in
51-
* ram_groups_ for RamMapper), and runs a mini-packing simulation for
52-
* non-RAM types using pin capacity and cluster placement feasibility
53-
* constraints to get required number of instances for each type.
70+
* Groups RAM atoms, assigns them to minimum-area types, and runs a
71+
* mini-packing simulation for non-RAM types using pin capacity and
72+
* cluster placement feasibility constraints to get required number of
73+
* instances for each type.
5474
*
75+
* @param prepacker
76+
* Used to query molecule structure and placement feasibility.
77+
* @param store_ram_groups
78+
* If true, the RAM groups computed as part of this estimate are
79+
* published to ram_groups_ for reuse by RamMapper. Callers that
80+
* only want the type instance estimate (e.g. for a fixed device,
81+
* where RamMapper must instead do its own capacity-aware grouping)
82+
* should pass false so ram_groups() is left untouched.
5583
* @return Map from logical block type to estimated cluster instance count.
5684
*/
5785
std::map<t_logical_block_type_ptr, size_t> estimate_resource_requirement(
58-
const Prepacker& prepacker);
86+
const Prepacker& prepacker,
87+
bool store_ram_groups = true);
5988

6089
/// @brief RAM groups computed during estimation; exposed via ram_groups()
6190
/// for reuse by RamMapper to avoid redundant grouping and area assignment.
6291
vtr::vector<LogicalRamGroupId, LogicalRamGroup> ram_groups_;
92+
93+
/// @brief Estimated cluster instance counts per logical block type;
94+
/// exposed via estimated_type_instance_counts().
95+
std::map<t_logical_block_type_ptr, size_t> estimated_num_type_instances_;
6396
};

0 commit comments

Comments
 (0)