Skip to content

Commit

Permalink
[Packer] Created GreedySeedSelector Class
Browse files Browse the repository at this point in the history
Cleaned up the way that new seed molecules were picked in the greedy
clusterer by abstracting it into a class. This will make it simpler to
build upon this or change how seeds are selected in the future.
  • Loading branch information
AlexandreSinger committed Nov 22, 2024
1 parent 33c131a commit e956917
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 214 deletions.
182 changes: 0 additions & 182 deletions vpr/src/pack/cluster_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1523,156 +1523,6 @@ t_molecule_stats calc_molecule_stats(const t_pack_molecule* molecule, const Atom
return molecule_stats;
}

std::vector<AtomBlockId> initialize_seed_atoms(const e_cluster_seed seed_type,
const t_molecule_stats& max_molecule_stats,
const Prepacker& prepacker,
const vtr::vector<AtomBlockId, float>& atom_criticality) {
const AtomNetlist& atom_nlist = g_vpr_ctx.atom().nlist;

//Put all atoms in seed list
std::vector<AtomBlockId> seed_atoms(atom_nlist.blocks().begin(), atom_nlist.blocks().end());

//Initially all gains are zero
vtr::vector<AtomBlockId, float> atom_gains(atom_nlist.blocks().size(), 0.);

if (seed_type == e_cluster_seed::TIMING) {
VTR_ASSERT(atom_gains.size() == atom_criticality.size());

//By criticality
atom_gains = atom_criticality;

} else if (seed_type == e_cluster_seed::MAX_INPUTS) {
//By number of used molecule input pins
for (auto blk : atom_nlist.blocks()) {
const t_pack_molecule* blk_mol = prepacker.get_atom_molecule(blk);
const t_molecule_stats molecule_stats = calc_molecule_stats(blk_mol, atom_nlist);
atom_gains[blk] = molecule_stats.num_used_ext_inputs;
}

} else if (seed_type == e_cluster_seed::BLEND) {
//By blended gain (criticality and inputs used)
for (auto blk : atom_nlist.blocks()) {
/* Score seed gain of each block as a weighted sum of timing criticality,
* number of tightly coupled blocks connected to it, and number of external inputs */
float seed_blend_fac = 0.5;

const t_pack_molecule* blk_mol = prepacker.get_atom_molecule(blk);
const t_molecule_stats molecule_stats = calc_molecule_stats(blk_mol, atom_nlist);
VTR_ASSERT(max_molecule_stats.num_used_ext_inputs > 0);

float blend_gain = (seed_blend_fac * atom_criticality[blk]
+ (1 - seed_blend_fac) * (molecule_stats.num_used_ext_inputs / max_molecule_stats.num_used_ext_inputs));
blend_gain *= (1 + 0.2 * (molecule_stats.num_blocks - 1));
atom_gains[blk] = blend_gain;
}

} else if (seed_type == e_cluster_seed::MAX_PINS || seed_type == e_cluster_seed::MAX_INPUT_PINS) {
//By pins per molecule (i.e. available pins on primitives, not pins in use)

for (auto blk : atom_nlist.blocks()) {
const t_pack_molecule* mol = prepacker.get_atom_molecule(blk);
const t_molecule_stats molecule_stats = calc_molecule_stats(mol, atom_nlist);

int molecule_pins = 0;
if (seed_type == e_cluster_seed::MAX_PINS) {
//All pins
molecule_pins = molecule_stats.num_pins;
} else {
VTR_ASSERT(seed_type == e_cluster_seed::MAX_INPUT_PINS);
//Input pins only
molecule_pins = molecule_stats.num_input_pins;
}

atom_gains[blk] = molecule_pins;
}

} else if (seed_type == e_cluster_seed::BLEND2) {
for (auto blk : atom_nlist.blocks()) {
const t_pack_molecule* mol = prepacker.get_atom_molecule(blk);
const t_molecule_stats molecule_stats = calc_molecule_stats(mol, atom_nlist);

float pin_ratio = vtr::safe_ratio<float>(molecule_stats.num_pins, max_molecule_stats.num_pins);
float input_pin_ratio = vtr::safe_ratio<float>(molecule_stats.num_input_pins, max_molecule_stats.num_input_pins);
float output_pin_ratio = vtr::safe_ratio<float>(molecule_stats.num_output_pins, max_molecule_stats.num_output_pins);
float used_ext_pin_ratio = vtr::safe_ratio<float>(molecule_stats.num_used_ext_pins, max_molecule_stats.num_used_ext_pins);
float used_ext_input_pin_ratio = vtr::safe_ratio<float>(molecule_stats.num_used_ext_inputs, max_molecule_stats.num_used_ext_inputs);
float used_ext_output_pin_ratio = vtr::safe_ratio<float>(molecule_stats.num_used_ext_outputs, max_molecule_stats.num_used_ext_outputs);
float num_blocks_ratio = vtr::safe_ratio<float>(molecule_stats.num_blocks, max_molecule_stats.num_blocks);
float criticality = atom_criticality[blk];

constexpr float PIN_WEIGHT = 0.;
constexpr float INPUT_PIN_WEIGHT = 0.5;
constexpr float OUTPUT_PIN_WEIGHT = 0.;
constexpr float USED_PIN_WEIGHT = 0.;
constexpr float USED_INPUT_PIN_WEIGHT = 0.2;
constexpr float USED_OUTPUT_PIN_WEIGHT = 0.;
constexpr float BLOCKS_WEIGHT = 0.2;
constexpr float CRITICALITY_WEIGHT = 0.1;

float gain = PIN_WEIGHT * pin_ratio
+ INPUT_PIN_WEIGHT * input_pin_ratio
+ OUTPUT_PIN_WEIGHT * output_pin_ratio

+ USED_PIN_WEIGHT * used_ext_pin_ratio
+ USED_INPUT_PIN_WEIGHT * used_ext_input_pin_ratio
+ USED_OUTPUT_PIN_WEIGHT * used_ext_output_pin_ratio

+ BLOCKS_WEIGHT * num_blocks_ratio
+ CRITICALITY_WEIGHT * criticality;

atom_gains[blk] = gain;
}

} else {
VPR_FATAL_ERROR(VPR_ERROR_PACK, "Unrecognized cluster seed type");
}

//Sort seeds in descending order of gain (i.e. highest gain first)
//
// Note that we use a *stable* sort here. It has been observed that different
// standard library implementations (e.g. gcc-4.9 vs gcc-5) use sorting algorithms
// which produce different orderings for seeds of equal gain (which is allowed with
// std::sort which does not specify how equal values are handled). Using a stable
// sort ensures that regardless of the underlying sorting algorithm the same seed
// order is produced regardless of compiler.
auto by_descending_gain = [&](const AtomBlockId lhs, const AtomBlockId rhs) {
return atom_gains[lhs] > atom_gains[rhs];
};
std::stable_sort(seed_atoms.begin(), seed_atoms.end(), by_descending_gain);

if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_CLUSTERING_BLOCK_CRITICALITIES)) {
print_seed_gains(getEchoFileName(E_ECHO_CLUSTERING_BLOCK_CRITICALITIES), seed_atoms, atom_gains, atom_criticality);
}

return seed_atoms;
}

t_pack_molecule* get_highest_gain_seed_molecule(int& seed_index,
const std::vector<AtomBlockId>& seed_atoms,
const Prepacker& prepacker,
const ClusterLegalizer& cluster_legalizer) {
while (seed_index < static_cast<int>(seed_atoms.size())) {
AtomBlockId blk_id = seed_atoms[seed_index++];

// Check if the atom has already been assigned to a cluster
if (!cluster_legalizer.is_atom_clustered(blk_id)) {
t_pack_molecule* best = nullptr;

t_pack_molecule* molecule = prepacker.get_atom_molecule(blk_id);
if (!cluster_legalizer.is_mol_clustered(molecule)) {
if (best == nullptr || (best->base_gain) < (molecule->base_gain)) {
best = molecule;
}
}
VTR_ASSERT(best != nullptr);
return best;
}
}

/*if it makes it to here , there are no more blocks available*/
return nullptr;
}

float get_molecule_gain(t_pack_molecule* molecule, std::map<AtomBlockId, float>& blk_gain, AttractGroupId cluster_attraction_group_id, AttractionInfo& attraction_groups, int num_molecule_failures) {
float gain;
int i;
Expand Down Expand Up @@ -1804,38 +1654,6 @@ std::map<const t_model*, std::vector<t_logical_block_type_ptr>> identify_primiti
return model_candidates;
}

void print_seed_gains(const char* fname, const std::vector<AtomBlockId>& seed_atoms, const vtr::vector<AtomBlockId, float>& atom_gain, const vtr::vector<AtomBlockId, float>& atom_criticality) {
FILE* fp = vtr::fopen(fname, "w");

const AtomContext& atom_ctx = g_vpr_ctx.atom();

//For prett formatting determine the maximum name length
int max_name_len = strlen("atom_block_name");
int max_type_len = strlen("atom_block_type");
for (auto blk_id : atom_ctx.nlist.blocks()) {
max_name_len = std::max(max_name_len, (int)atom_ctx.nlist.block_name(blk_id).size());

const t_model* model = atom_ctx.nlist.block_model(blk_id);
max_type_len = std::max(max_type_len, (int)strlen(model->name));
}

fprintf(fp, "%-*s %-*s %8s %8s\n", max_name_len, "atom_block_name", max_type_len, "atom_block_type", "gain", "criticality");
fprintf(fp, "\n");
for (auto blk_id : seed_atoms) {
std::string name = atom_ctx.nlist.block_name(blk_id);
fprintf(fp, "%-*s ", max_name_len, name.c_str());

const t_model* model = atom_ctx.nlist.block_model(blk_id);
fprintf(fp, "%-*s ", max_type_len, model->name);

fprintf(fp, "%*f ", std::max((int)strlen("gain"), 8), atom_gain[blk_id]);
fprintf(fp, "%*f ", std::max((int)strlen("criticality"), 8), atom_criticality[blk_id]);
fprintf(fp, "\n");
}

fclose(fp);
}

size_t update_pb_type_count(const t_pb* pb, std::map<t_pb_type*, int>& pb_type_count, size_t depth) {
size_t max_depth = depth;

Expand Down
12 changes: 0 additions & 12 deletions vpr/src/pack/cluster_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,16 +428,6 @@ t_pack_molecule* get_molecule_for_cluster(t_pb* cur_pb,
*/
t_molecule_stats calc_molecule_stats(const t_pack_molecule* molecule, const AtomNetlist& atom_nlist);

std::vector<AtomBlockId> initialize_seed_atoms(const e_cluster_seed seed_type,
const t_molecule_stats& max_molecule_stats,
const Prepacker& prepacker,
const vtr::vector<AtomBlockId, float>& atom_criticality);

t_pack_molecule* get_highest_gain_seed_molecule(int& seed_index,
const std::vector<AtomBlockId>& seed_atoms,
const Prepacker& prepacker,
const ClusterLegalizer& cluster_legalizer);

/*
* @brief Get gain of packing molecule into current cluster.
*
Expand All @@ -448,8 +438,6 @@ t_pack_molecule* get_highest_gain_seed_molecule(int& seed_index,
*/
float get_molecule_gain(t_pack_molecule* molecule, std::map<AtomBlockId, float>& blk_gain, AttractGroupId cluster_attraction_group_id, AttractionInfo& attraction_groups, int num_molecule_failures);

void print_seed_gains(const char* fname, const std::vector<AtomBlockId>& seed_atoms, const vtr::vector<AtomBlockId, float>& atom_gain, const vtr::vector<AtomBlockId, float>& atom_criticality);

/**
* @brief Score unclustered atoms that are two hops away from current cluster
*
Expand Down
35 changes: 15 additions & 20 deletions vpr/src/pack/greedy_clusterer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,16 @@

#include "greedy_clusterer.h"
#include <map>
#include <vector>
#include "atom_netlist.h"
#include "attraction_groups.h"
#include "cluster_legalizer.h"
#include "cluster_util.h"
#include "constraints_report.h"
#include "greedy_seed_selector.h"
#include "physical_types.h"
#include "prepack.h"
#include "vtr_vector.h"

GreedyClusterer::GreedyClusterer(const t_packer_opts& packer_opts,
const t_analysis_opts& analysis_opts,
Expand Down Expand Up @@ -106,7 +109,7 @@ GreedyClusterer::do_clustering(ClusterLegalizer& cluster_legalizer,

enum e_block_pack_status block_pack_status;

t_pack_molecule *istart, *next_molecule, *prev_molecule;
t_pack_molecule *next_molecule, *prev_molecule;

auto& device_ctx = g_vpr_ctx.mutable_device();

Expand All @@ -130,8 +133,6 @@ GreedyClusterer::do_clustering(ClusterLegalizer& cluster_legalizer,
* (eg. [A1, A2, ..]->[B1, B2, ..]->C implies cluster [A1, A2, ...] and C have a weak link) */
vtr::vector<LegalizationClusterId, std::vector<AtomNetId>> clb_inter_blk_nets(atom_netlist_.blocks().size());

istart = nullptr;

const t_molecule_stats max_molecule_stats = prepacker.calc_max_molecule_stats(atom_netlist_);

cluster_stats.num_molecules = prepacker.get_num_molecules();
Expand Down Expand Up @@ -170,18 +171,16 @@ GreedyClusterer::do_clustering(ClusterLegalizer& cluster_legalizer,
clustering_delay_calc, timing_info, atom_criticality);
}

// Assign gain scores to atoms and sort them based on the scores.
auto seed_atoms = initialize_seed_atoms(packer_opts_.cluster_seed_type,
max_molecule_stats,
prepacker,
atom_criticality);
// Create the greedy seed selector.
GreedySeedSelector seed_selector(atom_netlist_,
prepacker,
packer_opts_.cluster_seed_type,
max_molecule_stats,
atom_criticality);

/* index of next most timing critical block */
int seed_index = 0;
istart = get_highest_gain_seed_molecule(seed_index,
seed_atoms,
prepacker,
cluster_legalizer);
// Pick the first seed molecule.
t_pack_molecule* istart = seed_selector.get_next_seed(prepacker,
cluster_legalizer);

print_pack_status_header();

Expand All @@ -191,7 +190,6 @@ GreedyClusterer::do_clustering(ClusterLegalizer& cluster_legalizer,

while (istart != nullptr) {
bool is_cluster_legal = false;
int saved_seed_index = seed_index;
// The basic algorithm:
// 1) Try to put all the molecules in that you can without doing the
// full intra-lb route. Then do full legalization at the end.
Expand Down Expand Up @@ -333,10 +331,8 @@ GreedyClusterer::do_clustering(ClusterLegalizer& cluster_legalizer,

if (is_cluster_legal) {
// Pick new seed.
istart = get_highest_gain_seed_molecule(seed_index,
seed_atoms,
prepacker,
cluster_legalizer);
istart = seed_selector.get_next_seed(prepacker,
cluster_legalizer);
// Update cluster stats.
if (packer_opts_.timing_driven && num_blocks_hill_added > 0)
cluster_stats.blocks_since_last_analysis += num_blocks_hill_added;
Expand All @@ -350,7 +346,6 @@ GreedyClusterer::do_clustering(ClusterLegalizer& cluster_legalizer,
// If the cluster is not legal, requeue used mols.
num_used_type_instances[cluster_legalizer.get_cluster_type(legalization_cluster_id)]--;
total_clb_num--;
seed_index = saved_seed_index;
// Destroy the illegal cluster.
cluster_legalizer.destroy_cluster(legalization_cluster_id);
cluster_legalizer.compress();
Expand Down
Loading

0 comments on commit e956917

Please sign in to comment.