Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vpr][place] Placement move primitive refactoring #2659

Merged
merged 11 commits into from
Jul 29, 2024

Conversation

robluo
Copy link

@robluo robluo commented Jul 22, 2024

  1. In "net_cost_handler.cpp", there are three related global variables: net_cost, proposed_net_cost, and bb_updated_before. We can organize these by putting them into a struct called t_net_cost_data. You may also rename bb_updated_before to bb_update_status.

  2. In placement, we use two methods to update the bounding box: the 3D bounding box (also used for 2D architecture) and the per-layer bounding box. When a swap happens, multiple variables are updated, indicated by the ts (temporary swap) prefix. We can put all these variables into a class. Since only a subset is used depending on the bounding box type, you can examine how these variables are updated throughout the file and write methods for this class to update the appropriate ones.

  3. For runtime reasons, we initialize ts_nets_to_update to the number of nets in the netlist and use a variable num_affected_nets to indicate how many entries are valid. You can experiment to see how runtime is affected if we remove this variable and just reserve elements in this vector and use .size().

  4. The same approach is used for std::vector<t_pl_moved_block>& moved_blocks, which has as many entries as the total number of blocks, and we pass an integer to show how many entries are valid. You can also run an experiment to see how much runtime is affected if you remove this.

QoR bitcoin_miner_stratixiv_arch_timing (final wire length remains the same):
baseline: packing: 354.64, placement: 3706.12, routing 409.20, vpr flow: 4930.11
modified: packing: 356.83, placement: 3777.65, routing 412.23, vpr flow: 4994.61

@github-actions github-actions bot added VPR VPR FPGA Placement & Routing Tool lang-cpp C/C++ code labels Jul 22, 2024
@robluo robluo marked this pull request as ready for review July 24, 2024 03:49
@robluo robluo force-pushed the placement_move_primitive_robert_refactoring branch 2 times, most recently from 2e19dc4 to d8fe932 Compare July 24, 2024 18:44
@robluo robluo requested review from vaughnbetz and amin1377 July 24, 2024 20:47
Copy link
Contributor

@soheilshahrouz soheilshahrouz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments for all files other than net_cost_handler.cpp/.h

std::vector<t_pl_moved_block> moved_blocks;
std::unordered_set<t_pl_loc> moved_from;
std::unordered_set<t_pl_loc> moved_to;

std::vector<ClusterPinId> affected_pins;
size_t get_size_and_increment();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add doxygen documentation for this method.

* moved blocks: a list of moved blocks data structure with *
* information on the move. *
* [0...max_blocks-1] *
* affected_pins: pins affected by this move (used to *
* incrementally invalidate parts of the timing *
* graph. */
struct t_pl_blocks_to_be_moved {
explicit t_pl_blocks_to_be_moved(size_t max_blocks)
: moved_blocks(max_blocks) {}
explicit t_pl_blocks_to_be_moved(size_t max_blocks);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is probably a good idea to delete the default constructor if the objects of this type are always expected to be initialzed with a non-zero capacity

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also delete the copy constructor to avoid unintended copies.


t_pl_blocks_to_be_moved::t_pl_blocks_to_be_moved(size_t max_blocks){
moved_blocks.reserve(max_blocks);
moved_blocks.resize(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is calling resize() necessary?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is not necessary, the vector default constructor will set size to 0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is not necessary, the vector default constructor will set size to 0.

So I think it is better to remove it

vpr/src/place/move_transactions.cpp Outdated Show resolved Hide resolved
@@ -40,11 +51,11 @@ void apply_move_blocks(const t_pl_blocks_to_be_moved& blocks_affected) {

//Swap the blocks, but don't swap the nets or update place_ctx.grid_blocks
//yet since we don't know whether the swap will be accepted
for (int iblk = 0; iblk < blocks_affected.num_moved_blocks; ++iblk) {
ClusterBlockId blk = blocks_affected.moved_blocks[iblk].block_num;
for (const auto& block : blocks_affected.moved_blocks) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if the element type (t_pl_moved_block) is explicitly specified instead of using auto keyword, the code would be more readable, especially that the type name is not that long.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also suggest that the element name changes from "block" to "moved_block"

@@ -488,12 +488,12 @@ std::set<t_pl_loc> determine_locations_emptied_by_move(t_pl_blocks_to_be_moved&
std::set<t_pl_loc> moved_from;
std::set<t_pl_loc> moved_to;

for (int iblk = 0; iblk < blocks_affected.num_moved_blocks; ++iblk) {
for (const auto& block : blocks_affected.moved_blocks) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using the type name explicitly makes the code more readable

@@ -488,12 +488,12 @@ std::set<t_pl_loc> determine_locations_emptied_by_move(t_pl_blocks_to_be_moved&
std::set<t_pl_loc> moved_from;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocks_affected can be passed by const reference

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function can be implemented as member function in t_pl_blocks_to_be_moved

for (int i = 0; i < blocks_affected.num_moved_blocks; i++) {
floorplan_legal = cluster_floorplanning_legal(blocks_affected.moved_blocks[i].block_num,
blocks_affected.moved_blocks[i].new_loc);
for (const auto& block : blocks_affected.moved_blocks) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicit type name make the code more readable

@@ -1378,7 +1378,7 @@ static e_move_result try_swap(const t_annealing_state* state,
//
//Also find all the pins affected by the swap, and calculates new connection
//delays and timing costs and store them in proposed_* data structures.
int num_nets_affected = find_affected_nets_and_update_costs(
find_affected_nets_and_update_costs(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if this function call is written in two lines, it would be more readable:

find_affected_nets_and_update_costs(place_algorithm, delay_model, criticalities,
                                    blocks_affected, bb_delta_c, timing_delta_c);

@@ -1569,7 +1568,7 @@ static e_move_result try_swap(const t_annealing_state* state,
calculate_reward_and_process_outcome(placer_opts, move_outcome_stats,
delta_c, timing_bb_factor, move_generator);
} else {
// std::cout << "Group move delta cost: " << delta_c << std::endl;
// std::cout << "Group move delta cost: " << delta_c << std::endl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the else statement
I used it for debugging, but it somehow found its way into master

public:
void init(size_t num_nets, bool cube_bb);
void get_non_updatable_bb(const ClusterNetId& net);
bool is_driver(const t_physical_tile_type_ptr& blk_type, const ClusterPinId& blk_pin);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the implementation of this method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClusterPinId is an integer. Passing a pointer and an integer by reference only make the code harder to read. These arguments are not large objects and passing them by value does not introduce any overhead.

static vtr::Matrix<int> ts_layer_sink_pin_count;
/* [0...num_afftected_nets] -> net_id of the affected nets */
static std::vector<ClusterNetId> ts_nets_to_update;
struct TSInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete non-default constructors to avoid copying or moving objects of types declared in this namesapce.

const PlaceDelayModel* delay_model,
const PlacerCriticalities* criticalities,
const ClusterBlockId& blk_id,
const ClusterPinId& pin_id,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass ClusterBlockId and ClusterPinId by value. These types are just integers

* @brief This class is used to hide control flows needed to distinguish 2d and 3d placement
*/
class BBUpdater {
bool cube_bb = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to somehow differentiate member varaibles from arguments and local variable by naming them differently. For exmaple: cube_bb_ or m_cube_bb.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using private keyword make the code more readable.

}

void BBUpdater::get_non_updatable_bb(const ClusterNetId& net) {
if (cube_bb)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes single line if/else statement have braces and sometimes they don't. It is better to follow a consistent style. I think always using braces is better, even for single line if/else statements.

size_t last_size = ts_info.ts_nets_to_update.size();
VTR_ASSERT(last_size < ts_info.ts_nets_to_update.capacity());
ts_info.ts_nets_to_update.resize(last_size + 1);
ts_info.ts_nets_to_update[last_size] = net;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use push_back()?
The assertion can also compare capacity and size before adding the new element.

const PlaceDelayModel* delay_model,
const PlacerCriticalities* criticalities,
const ClusterBlockId& blk_id,
const ClusterPinId& pin_id,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass ClusterBlockId and ClusterPinId types by value

ts_layer_sink_pin_count[size_t(net_id)]);
}
static void set_bb_delta_cost(double& bb_delta_c) {
for (const auto& ts_net: ts_info.ts_nets_to_update) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClusterNetId is an integer, and it is not necessary to iterate over elements of this time by reference.
Using the type name explicitly make the code more readable.

for (int inet_affected = 0; inet_affected < num_nets_affected;
inet_affected++) {
ClusterNetId net_id = ts_nets_to_update[inet_affected];
for (const auto& ts_net : ts_info.ts_nets_to_update) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to previous comments.

ClusterNetId net_id = ts_nets_to_update[inet_affected];
proposed_net_cost[net_id] = -1;
bb_updated_before[net_id] = NetUpdateState::NOT_UPDATED_YET;
for (const auto& ts_net : ts_info.ts_nets_to_update) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type name
by value

@robluo robluo force-pushed the placement_move_primitive_robert_refactoring branch from d8fe932 to c7cbf33 Compare July 28, 2024 04:16
@vaughnbetz
Copy link
Contributor

Thanks! I'm going to merge this, but I have one more comment you can address in another PR:

(line 32 of move_transactions.cpp)
It would be good to comment that we need to remove the block from moved_to, and why.

@vaughnbetz vaughnbetz merged commit 36ed2b2 into master Jul 29, 2024
36 checks passed
@vaughnbetz vaughnbetz deleted the placement_move_primitive_robert_refactoring branch July 29, 2024 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lang-cpp C/C++ code VPR VPR FPGA Placement & Routing Tool
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants