Skip to content

Commit e4c11bd

Browse files
Merge branch 'master' into pack_per_net_bb_state
2 parents a78c83e + f894d8e commit e4c11bd

4 files changed

Lines changed: 280 additions & 102 deletions

File tree

libs/librrgraph/src/base/rr_graph_storage.cpp

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "vtr_assert.h"
66
#include "vtr_error.h"
77
#include "librrgraph_types.h"
8+
#include "vtr_sort.h"
89
#include "vtr_util.h"
910

1011
#include <algorithm>
@@ -194,23 +195,32 @@ void t_rr_graph_storage::init_fan_in() {
194195
}
195196
}
196197

197-
namespace {
198-
/// Functor for sorting edges according to destination node's ID.
199-
class edge_compare_dest_node {
200-
public:
201-
edge_compare_dest_node(const t_rr_graph_storage& rr_graph_storage) : rr_graph_storage_(rr_graph_storage) {}
198+
void t_rr_graph_storage::apply_edge_permutation(const std::vector<RREdgeId>& edge_indices) {
199+
// Generic lambda that allocates a 'vec'-sized new vector with all elements set to default value,
200+
// then builds the new vector to have rearranged elements from 'vec' and finally move the new vector
201+
// to replace vec. Essentially does a permutation on vec based on edge_indices.
202+
auto array_rearrange = [&edge_indices](auto& vec, auto default_value) {
203+
// Since vec could have any type, we need to figure out its type to allocate new_vec.
204+
// The scary std::remove_reference stuff does exactly that. This does nothing other than building a new 'vec' sized vector.
205+
typename std::remove_reference<decltype(vec)>::type new_vec(vec.size(), default_value);
202206

203-
bool operator()(RREdgeId lhs, RREdgeId rhs) const {
204-
RRNodeId lhs_dest_node = rr_graph_storage_.edge_sink_node(lhs);
205-
RRNodeId rhs_dest_node = rr_graph_storage_.edge_sink_node(rhs);
207+
size_t new_index = 0;
208+
for (RREdgeId edge_index : edge_indices) {
209+
RREdgeId new_edge_index = RREdgeId(new_index);
210+
new_vec[new_edge_index] = vec[edge_index];
206211

207-
return lhs_dest_node < rhs_dest_node;
208-
}
212+
new_index++;
213+
}
214+
VTR_ASSERT(new_index == vec.size());
215+
216+
vec = std::move(new_vec);
217+
};
209218

210-
private:
211-
const t_rr_graph_storage& rr_graph_storage_;
212-
};
213-
} // namespace
219+
array_rearrange(edge_src_node_, RRNodeId::INVALID());
220+
array_rearrange(edge_dest_node_, RRNodeId::INVALID());
221+
array_rearrange(edge_switch_, LIBRRGRAPH_UNDEFINED_VAL);
222+
array_rearrange(edge_remapped_, false);
223+
}
214224

215225
size_t t_rr_graph_storage::count_rr_switches(const std::vector<t_arch_switch_inf>& arch_switch_inf,
216226
t_arch_switch_fanin& arch_switch_fanins) {
@@ -222,7 +232,7 @@ size_t t_rr_graph_storage::count_rr_switches(const std::vector<t_arch_switch_inf
222232

223233
// Sort by destination node to collect per node/per switch fan in values
224234
// This sort is safe to do because partition_edges() has not been invoked yet.
225-
sort_edges(edge_compare_dest_node(*this));
235+
sort_edges_by_keys(vtr::sort_key(node_storage_.size(), [&](RREdgeId e) { return edge_dest_node_[e]; }));
226236

227237
// Collect the fan-in per switch type for each node in the graph
228238
// Record the unique switch type/fanin combinations
@@ -318,35 +328,6 @@ void t_rr_graph_storage::mark_edges_as_rr_switch_ids() {
318328
remapped_edges_ = true;
319329
}
320330

321-
namespace{
322-
/// Functor for sorting edges according to source node, with configurable edges coming first
323-
class edge_compare_src_node_and_configurable_first {
324-
public:
325-
edge_compare_src_node_and_configurable_first(const vtr::vector<RRSwitchId, t_rr_switch_inf>& rr_switch_inf, const t_rr_graph_storage& rr_graph_storage)
326-
: rr_switch_inf_(rr_switch_inf),
327-
rr_graph_storage_(rr_graph_storage) {}
328-
329-
bool operator()(RREdgeId lhs, RREdgeId rhs) const {
330-
331-
RRNodeId lhs_dest_node = rr_graph_storage_.edge_sink_node(lhs);
332-
RRNodeId lhs_src_node = rr_graph_storage_.edge_source_node(lhs);
333-
RRSwitchId lhs_switch_type = RRSwitchId(rr_graph_storage_.edge_switch(lhs));
334-
bool lhs_is_configurable = rr_switch_inf_[lhs_switch_type].configurable();
335-
336-
RRNodeId rhs_dest_node = rr_graph_storage_.edge_sink_node(rhs);
337-
RRNodeId rhs_src_node = rr_graph_storage_.edge_source_node(rhs);
338-
RRSwitchId rhs_switch_type = RRSwitchId(rr_graph_storage_.edge_switch(rhs));
339-
bool rhs_is_configurable = rr_switch_inf_[rhs_switch_type].configurable();
340-
341-
return std::make_tuple(lhs_src_node, !lhs_is_configurable, lhs_dest_node, lhs_switch_type) < std::make_tuple(rhs_src_node, !rhs_is_configurable, rhs_dest_node, rhs_switch_type);
342-
}
343-
344-
private:
345-
const vtr::vector<RRSwitchId, t_rr_switch_inf>& rr_switch_inf_;
346-
const t_rr_graph_storage& rr_graph_storage_;
347-
};
348-
} // namespace
349-
350331
void t_rr_graph_storage::partition_edges(const vtr::vector<RRSwitchId, t_rr_switch_inf>& rr_switches) {
351332
if (partitioned_) {
352333
return;
@@ -359,7 +340,11 @@ void t_rr_graph_storage::partition_edges(const vtr::vector<RRSwitchId, t_rr_swit
359340
// by assign_first_edges()
360341
// - Edges within a source node have the configurable edges before the
361342
// non-configurable edges.
362-
sort_edges(edge_compare_src_node_and_configurable_first(rr_switches, *this));
343+
// Keys are listed from the most significant to the least significant.
344+
sort_edges_by_keys(vtr::sort_key(node_storage_.size(), [&](RREdgeId e) { return edge_src_node_[e]; }),
345+
vtr::sort_key(2, [&](RREdgeId e) { return !rr_switches[RRSwitchId(edge_switch_[e])].configurable(); }),
346+
vtr::sort_key(node_storage_.size(), [&](RREdgeId e) { return edge_dest_node_[e]; }),
347+
vtr::sort_key(rr_switches.size(), [&](RREdgeId e) { return edge_switch_[e]; }));
363348

364349
partitioned_ = true;
365350

libs/librrgraph/src/base/rr_graph_storage.h

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#pragma once
22

3-
#include <algorithm>
43
#include <bitset>
54

65
#include "librrgraph_types.h"
@@ -15,6 +14,7 @@
1514
#include "rr_switch.h"
1615
#include "vtr_log.h"
1716
#include "vtr_memory.h"
17+
#include "vtr_sort.h"
1818
#include "vtr_strong_id_range.h"
1919
#include "vtr_array_view.h"
2020
#include <numeric>
@@ -886,48 +886,42 @@ class t_rr_graph_storage {
886886
/** @brief Validate that edge data is partitioned correctly.*/
887887
bool validate_node(RRNodeId node_id, const vtr::vector<RRSwitchId, t_rr_switch_inf>& rr_switches) const;
888888
bool validate(const vtr::vector<RRSwitchId, t_rr_switch_inf>& rr_switches) const;
889-
889+
890890
/**
891-
* @brief Sorts edges according to comparison_function. This is an expensive method that builds the edge array from scratch
892-
* and invalidates all the RREdgeIds. This is not an inplace sort, and it is very expensive.
893-
* You should not be calling this method more than once or twice in the entire program, definitely do not use it in a hot loop.
894-
* @tparam t_comp_func callable object with two size_t arguments. See 'edge_compare_dest_node' for example.
895-
* @param comparison_function Comparison function to order edges with.
891+
* @brief Sorts edges by one or more small integer keys.
892+
*
893+
* The result is the same as a stable sort by the tuple of keys, ties keep their
894+
* current order, but it is computed with stable counting sort passes instead of a
895+
* comparison sort.
896+
*
897+
* This is an expensive method that rebuilds the edge arrays from scratch and
898+
* invalidates all RREdgeIds. It should only be called a few times while the graph
899+
* is being built, never in a hot loop.
900+
*
901+
* Example, sorting by destination node:
902+
*
903+
* sort_edges_by_keys(vtr::sort_key(num_nodes, [&](RREdgeId e) { return edge_dest_node_[e]; }));
904+
*
905+
* Example, sorting by source node, and by destination node among edges that share
906+
* a source node:
907+
*
908+
* sort_edges_by_keys(vtr::sort_key(num_nodes, [&](RREdgeId e) { return edge_src_node_[e]; }),
909+
* vtr::sort_key(num_nodes, [&](RREdgeId e) { return edge_dest_node_[e]; }));
910+
*
911+
* @param keys One vtr::sort_key per sort criterion, listed from the most significant
912+
* to the least significant. Each is built with vtr::sort_key(num_keys, key_of),
913+
* where key_of maps an RREdgeId to a value smaller than num_keys.
896914
*/
897-
template <typename t_comp_func>
898-
void sort_edges(t_comp_func comparison_function) {
899-
915+
template <typename... KeyFns>
916+
void sort_edges_by_keys(const vtr::sort_key<KeyFns>&... keys) {
900917
size_t num_edges = edge_src_node_.size();
901918
vtr::StrongIdRange<RREdgeId> edge_range(RREdgeId(0), RREdgeId(num_edges));
902-
std::vector<RREdgeId> edge_indices(edge_range.begin(), edge_range.end());
903919

904-
std::stable_sort(edge_indices.begin(), edge_indices.end(), comparison_function);
905-
906-
// Generic lambda that allocates a 'vec'-sized new vector with all elements set to default value,
907-
// then builds the new vector to have rearranged elements from 'vec' and finally move the new vector
908-
// to replace vec. Essentially does a permutation on vec based on edge_indices.
909-
auto array_rearrange = [&edge_indices] (auto& vec, auto default_value) {
910-
911-
// Since vec could have any type, we need to figure out it's type to allocate new_vec.
912-
// The scary std::remove_reference stuff does exactly that. This does nothing other than building a new 'vec' sized vector.
913-
typename std::remove_reference<decltype(vec)>::type new_vec(vec.size(), default_value);
914-
915-
size_t new_index = 0;
916-
for (RREdgeId edge_index : edge_indices) {
917-
RREdgeId new_edge_index = RREdgeId(new_index);
918-
new_vec[new_edge_index] = vec[edge_index];
919-
920-
new_index++;
921-
}
922-
VTR_ASSERT(new_index == vec.size());
920+
// Sort the edge ids 0..num_edges-1 by the keys directly into edge_indices
921+
std::vector<RREdgeId> edge_indices(num_edges);
922+
vtr::stable_radix_sort(edge_range.begin(), edge_range.end(), edge_indices, keys...);
923923

924-
vec = std::move(new_vec);
925-
};
926-
927-
array_rearrange(edge_src_node_, RRNodeId::INVALID());
928-
array_rearrange(edge_dest_node_, RRNodeId::INVALID());
929-
array_rearrange(edge_switch_, LIBRRGRAPH_UNDEFINED_VAL);
930-
array_rearrange(edge_remapped_, false);
924+
apply_edge_permutation(edge_indices);
931925
}
932926

933927
/******************
@@ -979,6 +973,12 @@ class t_rr_graph_storage {
979973
*/
980974
void assign_first_edges();
981975

976+
/**
977+
* @brief Rearranges every edge array so that the new edge i is the old edge edge_indices[i].
978+
* Invalidates all RREdgeIds held elsewhere.
979+
*/
980+
void apply_edge_permutation(const std::vector<RREdgeId>& edge_indices);
981+
982982
/** @brief Verify that first_edge_ array correctly partitions rr edge data. */
983983
bool verify_first_edges() const;
984984

libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,31 +1147,31 @@ class RrGraphSerializer final : public uxsd::RrGraphBase<RrGraphContextTypes> {
11471147
return nullptr;
11481148
}
11491149
inline void finish_rr_graph_rr_edges(void*& /*ctx*/) final {
1150+
// The sink node and the switch id of every edge must be checked before the edges are
1151+
// partitioned. partition_edges() sorts the edges by those two values, so an out of range
1152+
// value read from the file would index outside the node and switch arrays.
1153+
// Only the source node is checked as the edges are read, in add_rr_edges_edge().
1154+
for (RREdgeId edge_id : rr_nodes_->all_edges()) {
1155+
size_t sink_node = size_t(rr_nodes_->edge_sink_node(edge_id));
1156+
size_t switch_id = size_t(rr_nodes_->edge_switch(edge_id));
1157+
if (sink_node >= rr_nodes_->size()) {
1158+
report_error(
1159+
"sink_node %zu is larger than rr_nodes.size() %zu",
1160+
sink_node, rr_nodes_->size());
1161+
}
1162+
1163+
if (switch_id >= rr_switch_inf_->size()) {
1164+
report_error(
1165+
"switch_id %zu is larger than num_rr_switches %zu",
1166+
switch_id, rr_switch_inf_->size());
1167+
}
1168+
}
1169+
11501170
// Partition the rr graph edges for efficient access to
11511171
// configurable/non-configurable edge subsets. Must be done after RR
11521172
// switches have been allocated.
11531173
rr_graph_builder_->mark_edges_as_rr_switch_ids();
11541174
rr_graph_builder_->partition_edges();
1155-
1156-
for (size_t source_node = 0; source_node < rr_nodes_->size(); ++source_node) {
1157-
int num_edges = rr_nodes_->num_edges(RRNodeId(source_node));
1158-
for (int iconn = 0; iconn < num_edges; ++iconn) {
1159-
size_t sink_node = size_t(rr_nodes_->edge_sink_node(RRNodeId(source_node), iconn));
1160-
size_t switch_id = rr_nodes_->edge_switch(RRNodeId(source_node), iconn);
1161-
if (sink_node >= rr_nodes_->size()) {
1162-
report_error(
1163-
"sink_node %zu is larger than rr_nodes.size() %zu",
1164-
sink_node, rr_nodes_->size());
1165-
}
1166-
1167-
if (switch_id >= rr_switch_inf_->size()) {
1168-
report_error(
1169-
"switch_id %zu is larger than num_rr_switches %zu",
1170-
switch_id, rr_switch_inf_->size());
1171-
}
1172-
1173-
}
1174-
}
11751175
}
11761176

11771177
inline EdgeWalker get_rr_graph_rr_edges(void*& /*ctx*/) final {

0 commit comments

Comments
 (0)