From e95691734598d213bd3a2f62b0755e019e057a62 Mon Sep 17 00:00:00 2001 From: AlexandreSinger Date: Wed, 20 Nov 2024 16:26:46 -0500 Subject: [PATCH] [Packer] Created GreedySeedSelector Class 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. --- vpr/src/pack/cluster_util.cpp | 182 -------------------- vpr/src/pack/cluster_util.h | 12 -- vpr/src/pack/greedy_clusterer.cpp | 35 ++-- vpr/src/pack/greedy_seed_selector.cpp | 237 ++++++++++++++++++++++++++ vpr/src/pack/greedy_seed_selector.h | 97 +++++++++++ 5 files changed, 349 insertions(+), 214 deletions(-) create mode 100644 vpr/src/pack/greedy_seed_selector.cpp create mode 100644 vpr/src/pack/greedy_seed_selector.h diff --git a/vpr/src/pack/cluster_util.cpp b/vpr/src/pack/cluster_util.cpp index 7597516aa4..75a1ce82a5 100644 --- a/vpr/src/pack/cluster_util.cpp +++ b/vpr/src/pack/cluster_util.cpp @@ -1523,156 +1523,6 @@ t_molecule_stats calc_molecule_stats(const t_pack_molecule* molecule, const Atom return molecule_stats; } -std::vector initialize_seed_atoms(const e_cluster_seed seed_type, - const t_molecule_stats& max_molecule_stats, - const Prepacker& prepacker, - const vtr::vector& atom_criticality) { - const AtomNetlist& atom_nlist = g_vpr_ctx.atom().nlist; - - //Put all atoms in seed list - std::vector seed_atoms(atom_nlist.blocks().begin(), atom_nlist.blocks().end()); - - //Initially all gains are zero - vtr::vector 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(molecule_stats.num_pins, max_molecule_stats.num_pins); - float input_pin_ratio = vtr::safe_ratio(molecule_stats.num_input_pins, max_molecule_stats.num_input_pins); - float output_pin_ratio = vtr::safe_ratio(molecule_stats.num_output_pins, max_molecule_stats.num_output_pins); - float used_ext_pin_ratio = vtr::safe_ratio(molecule_stats.num_used_ext_pins, max_molecule_stats.num_used_ext_pins); - float used_ext_input_pin_ratio = vtr::safe_ratio(molecule_stats.num_used_ext_inputs, max_molecule_stats.num_used_ext_inputs); - float used_ext_output_pin_ratio = vtr::safe_ratio(molecule_stats.num_used_ext_outputs, max_molecule_stats.num_used_ext_outputs); - float num_blocks_ratio = vtr::safe_ratio(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& seed_atoms, - const Prepacker& prepacker, - const ClusterLegalizer& cluster_legalizer) { - while (seed_index < static_cast(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& blk_gain, AttractGroupId cluster_attraction_group_id, AttractionInfo& attraction_groups, int num_molecule_failures) { float gain; int i; @@ -1804,38 +1654,6 @@ std::map> identify_primiti return model_candidates; } -void print_seed_gains(const char* fname, const std::vector& seed_atoms, const vtr::vector& atom_gain, const vtr::vector& 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& pb_type_count, size_t depth) { size_t max_depth = depth; diff --git a/vpr/src/pack/cluster_util.h b/vpr/src/pack/cluster_util.h index a5bcd7f1fe..c794daf066 100644 --- a/vpr/src/pack/cluster_util.h +++ b/vpr/src/pack/cluster_util.h @@ -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 initialize_seed_atoms(const e_cluster_seed seed_type, - const t_molecule_stats& max_molecule_stats, - const Prepacker& prepacker, - const vtr::vector& atom_criticality); - -t_pack_molecule* get_highest_gain_seed_molecule(int& seed_index, - const std::vector& seed_atoms, - const Prepacker& prepacker, - const ClusterLegalizer& cluster_legalizer); - /* * @brief Get gain of packing molecule into current cluster. * @@ -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& blk_gain, AttractGroupId cluster_attraction_group_id, AttractionInfo& attraction_groups, int num_molecule_failures); -void print_seed_gains(const char* fname, const std::vector& seed_atoms, const vtr::vector& atom_gain, const vtr::vector& atom_criticality); - /** * @brief Score unclustered atoms that are two hops away from current cluster * diff --git a/vpr/src/pack/greedy_clusterer.cpp b/vpr/src/pack/greedy_clusterer.cpp index 997d21e7cd..98ca424362 100644 --- a/vpr/src/pack/greedy_clusterer.cpp +++ b/vpr/src/pack/greedy_clusterer.cpp @@ -38,13 +38,16 @@ #include "greedy_clusterer.h" #include +#include #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, @@ -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(); @@ -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> 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(); @@ -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(); @@ -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. @@ -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; @@ -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(); diff --git a/vpr/src/pack/greedy_seed_selector.cpp b/vpr/src/pack/greedy_seed_selector.cpp new file mode 100644 index 0000000000..5036d2d2bd --- /dev/null +++ b/vpr/src/pack/greedy_seed_selector.cpp @@ -0,0 +1,237 @@ +/** + * @file + * @author Alex Singer + * @date November 2024 + * @brief The definitions of the Greedy Seed Selector class. + */ + +#include "greedy_seed_selector.h" + +#include +#include "atom_netlist.h" +#include "cluster_legalizer.h" +#include "cluster_util.h" +#include "echo_files.h" +#include "prepack.h" +#include "vpr_error.h" +#include "vpr_types.h" +#include "vtr_assert.h" +#include "vtr_math.h" +#include "vtr_vector.h" + +/** + * @brief Helper method that computes the seed gain of the give atom block. + * + * The seed_type variable selects which algorithm to use to compute the seed + * gain. + */ +static inline float get_seed_gain(AtomBlockId blk_id, + const AtomNetlist& atom_netlist, + const Prepacker& prepacker, + const e_cluster_seed seed_type, + const t_molecule_stats& max_molecule_stats, + const vtr::vector& atom_criticality) { + switch (seed_type) { + // By criticality + case e_cluster_seed::TIMING: + return atom_criticality[blk_id]; + // By number of used molecule input pins + case e_cluster_seed::MAX_INPUTS: + { + const t_pack_molecule* blk_mol = prepacker.get_atom_molecule(blk_id); + const t_molecule_stats molecule_stats = calc_molecule_stats(blk_mol, atom_netlist); + return molecule_stats.num_used_ext_inputs; + } + // By blended gain (criticality and inputs used) + case e_cluster_seed::BLEND: + { + // 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.5f; + + const t_pack_molecule* blk_mol = prepacker.get_atom_molecule(blk_id); + const t_molecule_stats molecule_stats = calc_molecule_stats(blk_mol, atom_netlist); + VTR_ASSERT(max_molecule_stats.num_used_ext_inputs > 0); + + float used_ext_input_pin_ratio = vtr::safe_ratio(molecule_stats.num_used_ext_inputs, max_molecule_stats.num_used_ext_inputs); + float blend_gain = (seed_blend_fac * atom_criticality[blk_id] + + (1 - seed_blend_fac) * used_ext_input_pin_ratio); + blend_gain *= (1 + 0.2 * (molecule_stats.num_blocks - 1)); + return blend_gain; + } + // By pins per molecule (i.e. available pins on primitives, not pins in use) + case e_cluster_seed::MAX_PINS: + case e_cluster_seed::MAX_INPUT_PINS: + { + const t_pack_molecule* blk_mol = prepacker.get_atom_molecule(blk_id); + const t_molecule_stats molecule_stats = calc_molecule_stats(blk_mol, atom_netlist); + + 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; + } + + return molecule_pins; + } + case e_cluster_seed::BLEND2: + { + const t_pack_molecule* mol = prepacker.get_atom_molecule(blk_id); + const t_molecule_stats molecule_stats = calc_molecule_stats(mol, atom_netlist); + + float pin_ratio = vtr::safe_ratio(molecule_stats.num_pins, max_molecule_stats.num_pins); + float input_pin_ratio = vtr::safe_ratio(molecule_stats.num_input_pins, max_molecule_stats.num_input_pins); + float output_pin_ratio = vtr::safe_ratio(molecule_stats.num_output_pins, max_molecule_stats.num_output_pins); + float used_ext_pin_ratio = vtr::safe_ratio(molecule_stats.num_used_ext_pins, max_molecule_stats.num_used_ext_pins); + float used_ext_input_pin_ratio = vtr::safe_ratio(molecule_stats.num_used_ext_inputs, max_molecule_stats.num_used_ext_inputs); + float used_ext_output_pin_ratio = vtr::safe_ratio(molecule_stats.num_used_ext_outputs, max_molecule_stats.num_used_ext_outputs); + float num_blocks_ratio = vtr::safe_ratio(molecule_stats.num_blocks, max_molecule_stats.num_blocks); + float criticality = atom_criticality[blk_id]; + + 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; + + return gain; + } + default: + VPR_FATAL_ERROR(VPR_ERROR_PACK, "Unrecognized cluster seed type"); + return 0.f; + } +} + +/** + * @brief Helper method to print the seed gains of each Atom block and their + * criticalities. + */ +static inline void print_seed_gains(const char* fname, + const std::vector& seed_atoms, + const vtr::vector& atom_gain, + const vtr::vector& atom_criticality, + const AtomNetlist& atom_netlist) { + FILE* fp = vtr::fopen(fname, "w"); + + // For pretty 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_netlist.blocks()) { + max_name_len = std::max(max_name_len, (int)atom_netlist.block_name(blk_id).size()); + + const t_model* model = atom_netlist.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_netlist.block_name(blk_id); + fprintf(fp, "%-*s ", max_name_len, name.c_str()); + + const t_model* model = atom_netlist.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); +} + +GreedySeedSelector::GreedySeedSelector(const AtomNetlist& atom_netlist, + const Prepacker& prepacker, + const e_cluster_seed seed_type, + const t_molecule_stats& max_molecule_stats, + const vtr::vector& atom_criticality) + : seed_atoms_(atom_netlist.blocks().begin(), atom_netlist.blocks().end()) { + // Seed atoms list is initialized with all atoms in the atom netlist. + + // Maintain a lookup table of the seed gain for each atom. This will be + // used to sort the seed atoms. + // Initially all gains are zero. + vtr::vector atom_gains(atom_netlist.blocks().size(), 0.f); + + // Get the seed gain of each atom. + for (AtomBlockId blk_id : atom_netlist.blocks()) { + atom_gains[blk_id] = get_seed_gain(blk_id, + atom_netlist, + prepacker, + seed_type, + max_molecule_stats, + atom_criticality); + } + + // Sort seeds in descending order of seed gain (i.e. highest seed 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); + + // Print the seed gains if requested. + if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_CLUSTERING_BLOCK_CRITICALITIES)) { + print_seed_gains(getEchoFileName(E_ECHO_CLUSTERING_BLOCK_CRITICALITIES), + seed_atoms_, atom_gains, atom_criticality, atom_netlist); + } + + // Set the starting seed index (the index of the first molecule to propose). + // The index of the first seed to propose is the first molecule in the + // seed atoms vector (i.e. the one with the highest seed gain). + seed_index_ = 0; +} + +t_pack_molecule* GreedySeedSelector::get_next_seed(const Prepacker& prepacker, + const ClusterLegalizer& cluster_legalizer) { + while (seed_index_ < seed_atoms_.size()) { + // Get the current seed atom at the seed index and increment the + // seed index. + // All previous seed indices have been either proposed already or + // are already clustered. This process assumes that once an atom + // is clustered it will never become unclustered. + AtomBlockId seed_blk_id = seed_atoms_[seed_index_++]; + + // If this atom has been clustered, it cannot be proposed as a seed. + // Skip to the next seed. + if (cluster_legalizer.is_atom_clustered(seed_blk_id)) + continue; + + // Get the molecule that contains this atom and return it as the + // next seed. + t_pack_molecule* seed_molecule = prepacker.get_atom_molecule(seed_blk_id); + VTR_ASSERT(!cluster_legalizer.is_mol_clustered(seed_molecule)); + return seed_molecule; + } + + // If the previous loop does not return a molecule, it implies that all + // atoms have been clustered or have already been proposed as a seed. + // Return nullptr to signify that there are no further seeds. + return nullptr; +} + diff --git a/vpr/src/pack/greedy_seed_selector.h b/vpr/src/pack/greedy_seed_selector.h new file mode 100644 index 0000000000..f6eee80da4 --- /dev/null +++ b/vpr/src/pack/greedy_seed_selector.h @@ -0,0 +1,97 @@ +/** + * @file + * @author Alex Singer + * @date November 2024 + * @brief The declaration of the greedy seed selector class which selects the + * seed molecules for starting new clusters in the greedy clusterer. + */ + +#pragma once + +#include "vpr_types.h" + +// Forward declarations +class AtomNetlist; +class ClusterLegalizer; +class Prepacker; +struct t_molecule_stats; + +/** + * @brief A selector class which will propose good seed values to start new + * clusters in the greedy clusterer. + * + * In greedy clustering algorithms, the order in which clusters are generated + * can have an effect on the quality of the clustering. This class proposes + * good seed molecules based on heuristics which give each molecule a "seed + * gain". This class will not propose a molecule which it has already proposed + * or has already been clustered. + */ +class GreedySeedSelector { +public: + /** + * @brief Constructor of the Greedy Seed Selector class. Pre-computes the + * gains of each molecule internally to make getting seeds later very + * quick. + * + * @param atom_netlist + * The netlist of atoms to cluster. + * @param prepacker + * The prepacker used to generate pack-pattern molecules of the + * atoms in the netlist. + * @param seed_type + * Controls the algorithm used to compute the seed gain for + * each molecule. + * @param max_molecule_stats + * The maximum stats over all molecules. Used for normalizing + * terms in the gain. + * @param atom_criticality + * The timing criticality of each atom. + */ + GreedySeedSelector(const AtomNetlist& atom_netlist, + const Prepacker& prepacker, + const e_cluster_seed seed_type, + const t_molecule_stats& max_molecule_stats, + const vtr::vector& atom_criticality); + + /** + * @brief Propose a new seed molecule to start a new cluster with. If no + * unclustered molecules exist, will return nullptr. + * + * This method will never return a molecule which has already been clustered + * (according to the cluster legalizer) and will never propose a molecule + * that it already proposed. + * + * This method assumes that once a molecule is clustered, it will never be + * unclustered. + * + * @param prepacker + * The prepacker object that stores the molecules. + * @param cluster_legalizer + * The cluster legalizer object that is used to create the + * clusters. This is used to check if a molecule has already + * been clustered or not. + */ + t_pack_molecule* get_next_seed(const Prepacker& prepacker, + const ClusterLegalizer& cluster_legalizer); + + // TODO: Maybe create an update_seed_gains method to update the seed atoms + // list using current clustering information. + +private: + + /// @brief The index of the next seed to propose in the seed_atoms vector. + /// This is set to 0 in the constructor and incremented as more seeds + /// are proposed. + size_t seed_index_; + + /// @brief A list of seed atoms, sorted in decreasing order of gain. This + /// is computed in the constructor and is traversed when a new seed + /// is being proposed. + // FIXME: This should really be seed molecules. It looks like the only + // reason it isn't is because of the atom criticality. May want to + // create the concept of molecule criticality. Currently, the max + // criticality of any block in the molecule is technically being + // used. + std::vector seed_atoms_; +}; +