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

✨ Multithreading support for gold #529

Merged
merged 47 commits into from
Oct 7, 2024
Merged
Changes from 38 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
eb9411b
working solution
simon1hofmann Aug 30, 2024
c0ffd5b
Merge branch 'cda-tum:main' into gold_cost_functions
simon1hofmann Aug 30, 2024
7052f8c
:memo: Update pyfiction docstrings
actions-user Aug 30, 2024
0cfac62
Merge branch 'cda-tum:main' into gold_cost_functions
simon1hofmann Sep 3, 2024
a228dfb
structure
simon1hofmann Sep 3, 2024
05e474a
Merge remote-tracking branch 'origin/gold_cost_functions' into gold_c…
simon1hofmann Sep 3, 2024
152a335
cost objectives
simon1hofmann Sep 9, 2024
470771d
:memo: Update pyfiction docstrings
actions-user Sep 9, 2024
d999c8f
add bindings
simon1hofmann Sep 9, 2024
8d1173c
clean-up
simon1hofmann Sep 9, 2024
f11e096
benchmark
simon1hofmann Sep 9, 2024
c1a4fc0
:memo: Update pyfiction docstrings
actions-user Sep 9, 2024
1deb773
Merge branch 'main' into gold_cost_functions
simon1hofmann Sep 9, 2024
5203e35
increase coverage
simon1hofmann Sep 9, 2024
6dcaa86
Merge remote-tracking branch 'origin/gold_cost_functions' into gold_c…
simon1hofmann Sep 9, 2024
ca245c5
clang-tidy
simon1hofmann Sep 9, 2024
e777d29
small fix
simon1hofmann Sep 10, 2024
c205b2a
code review
simon1hofmann Sep 13, 2024
c2b7556
:memo: Update pyfiction docstrings
actions-user Sep 13, 2024
4d78d12
Merge branch 'main' into gold_cost_functions
simon1hofmann Sep 13, 2024
bba3919
bug fixes
simon1hofmann Sep 13, 2024
c67585b
Merge remote-tracking branch 'origin/gold_cost_functions' into gold_c…
simon1hofmann Sep 13, 2024
244f152
:memo: Update pyfiction docstrings
actions-user Sep 13, 2024
c26e5db
formatting
simon1hofmann Sep 13, 2024
7abf365
missing header
simon1hofmann Sep 13, 2024
27199a4
100% test coverage
simon1hofmann Sep 13, 2024
d13581f
more tests
simon1hofmann Sep 13, 2024
32a63f3
multithreading
simon1hofmann Sep 23, 2024
af4ee2a
:memo: Update pyfiction docstrings
actions-user Sep 23, 2024
584f824
Merge branch 'main' into gold_multithreading
simon1hofmann Sep 23, 2024
c8823a5
🎨 Incorporated pre-commit fixes
pre-commit-ci[bot] Sep 23, 2024
71e119c
Update graph_oriented_layout_design.cpp
simon1hofmann Sep 23, 2024
8cb66fe
🎨 Incorporated pre-commit fixes
pre-commit-ci[bot] Sep 23, 2024
8ff9a94
Update graph_oriented_layout_design.cpp
simon1hofmann Sep 23, 2024
6abf906
🎨 Incorporated pre-commit fixes
pre-commit-ci[bot] Sep 23, 2024
430008a
Update network_blueprints.hpp
simon1hofmann Sep 23, 2024
8eead9a
use atomics instead of mutex
simon1hofmann Sep 24, 2024
e9b4960
:memo: Update pyfiction docstrings
actions-user Sep 24, 2024
23b4ab8
rerun benchmarks
simon1hofmann Sep 25, 2024
ee0393e
remove empty statement
simon1hofmann Sep 25, 2024
06cb113
Jan's feedback
simon1hofmann Sep 26, 2024
344c138
Apply suggestions from code review
simon1hofmann Oct 7, 2024
ea21dc4
🎨 Incorporated pre-commit fixes
pre-commit-ci[bot] Oct 7, 2024
8e0e6d6
Merge branch 'main' into gold_multithreading
simon1hofmann Oct 7, 2024
96b281f
:memo: Update pyfiction docstrings
actions-user Oct 7, 2024
7da09c2
Feedback from Marcel
simon1hofmann Oct 7, 2024
949d7ce
:memo: Update pyfiction docstrings
actions-user Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -70,8 +70,8 @@ inline void graph_oriented_layout_design(pybind11::module& m)
DOC(fiction_graph_oriented_layout_design_params_return_first))
.def_readwrite("planar", &fiction::graph_oriented_layout_design_params::planar,
DOC(fiction_graph_oriented_layout_design_params_planar))

;
.def_readwrite("enable_multithreading", &fiction::graph_oriented_layout_design_params::enable_multithreading,
DOC(fiction_graph_oriented_layout_design_params_enable_multithreading));

py::class_<fiction::graph_oriented_layout_design_stats>(m, "graph_oriented_layout_design_stats",
DOC(fiction_graph_oriented_layout_design_stats))
25 changes: 23 additions & 2 deletions bindings/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp
Original file line number Diff line number Diff line change
@@ -6094,8 +6094,6 @@ static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_max_pl

static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_ntk = R"doc(The network to be placed and routed.)doc";

static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_num_evaluated_paths = R"doc(Count evaluated paths in the search space graphs.)doc";

static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_num_search_space_graphs = R"doc(Number of search space graphs.)doc";

static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_num_search_space_graphs_high_efficiency = R"doc(In high-efficiency mode, only 2 search space graphs are used)doc";
@@ -6200,6 +6198,8 @@ static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_start

static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_timeout = R"doc(Timeout limit (in ms).)doc";

static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_timeout_limit_reached = R"doc(Timeout limit reached.)doc";

static const char *__doc_fiction_detail_graph_oriented_layout_design_impl_valid_layout =
R"doc(Validates the given layout based on the nodes in the network and their
mappings in the node dictionary. It checks if the placement of nodes
@@ -11160,6 +11160,27 @@ wider exploration increases the chance of finding optimal layouts but
also extends runtime. When a solution is found in any graph, its cost
is used to prune the remaining graphs.)doc";

static const char *__doc_fiction_graph_oriented_layout_design_params_enable_multithreading =
R"doc(BETA feature: Flag to enable or disable multithreading during the
execution of the layout design algorithm.

When set to `true`, the algorithm will utilize multiple threads to
process different search space graphs in parallel, improving
performance by distributing the workload across available CPU cores.
If set to `false`, the algorithm will run sequentially in a single
thread.

Only recommended for HIGH_EFFORT and HIGHEST_EFFORT modes and complex
networks (>100 nodes).

Enabling multithreading can significantly speed up the algorithm,
especially when using multiple search space graphs and dealing with
complex networks, by concurrently expanding them. However, it may
introduce additional overhead for thread synchronization and can
increase memory usage.

Default value: `false`)doc";

static const char *__doc_fiction_graph_oriented_layout_design_params_mode = R"doc(The effort mode used. Defaults to HIGH_EFFORT.)doc";

static const char *__doc_fiction_graph_oriented_layout_design_params_num_vertex_expansions =
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ def test_graph_oriented_layout_design_with_different_parameters(self):
params.num_vertex_expansions = 5
params.planar = False
params.cost = gold_cost_objective.WIRES
params.enable_multithreading = False

layout = graph_oriented_layout_design(network, params)

1 change: 1 addition & 0 deletions cli/cmd/physical_design/gold.hpp
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ class gold_command : public command
add_flag("--return_first,-r", ps.return_first,
"Terminate on the first found layout; reduces runtime but might sacrifice result quality");
add_flag("--planar,-p", ps.planar, "Enable planar layout generation");
add_flag("--multithreading,-m", ps.enable_multithreading, "Enable multithreading (beta feature)");
add_flag("--verbose,-v", ps.verbose, "Be verbose");
}

Original file line number Diff line number Diff line change
@@ -32,8 +32,10 @@
#include <cstddef>
#include <cstdint>
#include <functional>
#include <future>
#include <iostream>
#include <limits>
#include <mutex>
#include <optional>
#include <queue>
#include <stdexcept>
@@ -136,6 +138,23 @@ struct graph_oriented_layout_design_params
* The cost objective used. Defaults to AREA
*/
cost_objective cost = AREA;
/**
* BETA feature:
* Flag to enable or disable multithreading during the execution of the layout design algorithm.
*
* When set to `true`, the algorithm will utilize multiple threads to process different search space graphs in
* parallel, improving performance by distributing the workload across available CPU cores. If set to `false`, the
* algorithm will run sequentially in a single thread.
*
* Only recommended for HIGH_EFFORT and HIGHEST_EFFORT modes and complex networks (>100 nodes).
*
* Enabling multithreading can significantly speed up the algorithm, especially when using multiple search space
* graphs and dealing with complex networks, by concurrently expanding them. However, it may introduce additional
* overhead for thread synchronization and can increase memory usage.
*
* Default value: `false`
*/
bool enable_multithreading = false;
/**
* Verbosity.
*/
@@ -664,7 +683,8 @@ class graph_oriented_layout_design_impl
pst{st},
custom_cost_objective{custom},
timeout{ps.timeout},
start{std::chrono::high_resolution_clock::now()}
start{std::chrono::high_resolution_clock::now()},
num_search_space_graphs{0}
{
ntk.substitute_po_signals();
}
@@ -698,45 +718,112 @@ class graph_oriented_layout_design_impl
{
timeout = 10000u;
}
bool timeout_limit_reached = false;

// main A* loop
// main loop
while (!timeout_limit_reached)
{
for (auto& ssg : ssg_vec)
// if multithreading is enabled
if (ps.enable_multithreading)
{
if (ssg.frontier_flag)
// mutex to protect shared resources (if necessary)
std::mutex mtx;
std::vector<std::future<std::optional<Lyt>>> futures;

// process `ssg_vec` in parallel using std::async
for (auto& ssg : ssg_vec)
{
num_evaluated_paths++;
const auto expansion = expand(ssg);
if (expansion.second)
{
best_lyt = *expansion.second;
restore_names(ssg.network, best_lyt);
futures.emplace_back(std::async(
std::launch::async,
[&]() -> std::optional<Lyt> // return std::optional<layout>
{
if (ssg.frontier_flag)
{
const auto expansion = expand(ssg);
if (expansion.second)
{
const std::lock_guard<std::mutex> lock(mtx); // protect access to shared data
best_lyt = *expansion.second;
restore_names(ssg.network, best_lyt);

// statistical information
pst.x_size = best_lyt.x() + 1;
pst.y_size = best_lyt.y() + 1;
pst.num_gates = best_lyt.num_gates();
pst.num_wires = best_lyt.num_wires();
pst.num_crossings = best_lyt.num_crossings();

if (ps.return_first)
{
return best_lyt; // return the layout if ps.return_first is true
}
}

// statistical information
pst.x_size = best_lyt.x() + 1;
pst.y_size = best_lyt.y() + 1;
pst.num_gates = best_lyt.num_gates();
pst.num_wires = best_lyt.num_wires();
pst.num_crossings = best_lyt.num_crossings();
// update costs and frontier
for (const auto& [next, cost] : expansion.first)
{
if (ssg.cost_so_far.find(next) == ssg.cost_so_far.cend() ||
cost < ssg.cost_so_far[next])
{
ssg.cost_so_far[next] = cost;
double priority = cost;
ssg.frontier.put(next, priority);
}
}
}
return std::nullopt; // return no layout found
}));
}

if (ps.return_first)
{
return best_lyt;
}
// Check the futures for the result
for (auto& future : futures)
{
auto result = future.get(); // blocking wait to get the result from the future
if (result)
{
return *result; // return the first found layout if ps.return_first is true
}
for (const auto& [next, cost] : expansion.first)
}
}
else
{
// single-threaded version
for (auto& ssg : ssg_vec)
{
if (ssg.frontier_flag)
{
if (ssg.cost_so_far.find(next) == ssg.cost_so_far.cend() || cost < ssg.cost_so_far[next])
const auto expansion = expand(ssg);
if (expansion.second)
{
best_lyt = *expansion.second;
restore_names(ssg.network, best_lyt);

// statistical information
pst.x_size = best_lyt.x() + 1;
pst.y_size = best_lyt.y() + 1;
pst.num_gates = best_lyt.num_gates();
pst.num_wires = best_lyt.num_wires();
pst.num_crossings = best_lyt.num_crossings();

if (ps.return_first)
{
return best_lyt; // return the layout if ps.return_first is true
}
}

for (const auto& [next, cost] : expansion.first)
{
ssg.cost_so_far[next] = cost;
double priority = cost;
ssg.frontier.put(next, priority);
if (ssg.cost_so_far.find(next) == ssg.cost_so_far.cend() || cost < ssg.cost_so_far[next])
{
ssg.cost_so_far[next] = cost;
double priority = cost;
ssg.frontier.put(next, priority);
}
}
}
}
}

// update current_vertex and frontier_flag (non-parallel for now)
for (auto& ssg : ssg_vec)
{
if (ssg.frontier_flag)
@@ -752,6 +839,7 @@ class graph_oriented_layout_design_impl
}
}

// check if timeout is reached or solution found
timeout_limit_reached =
std::none_of(ssg_vec.cbegin(), ssg_vec.cend(), [](const auto& ssg) { return ssg.frontier_flag; });

@@ -761,7 +849,7 @@ class graph_oriented_layout_design_impl

if (duration_ms >= timeout)
{
// Terminate the algorithm if the specified timeout was set or a solution was found in low-effort mode
// terminate the algorithm if the specified timeout was set or a solution was found in low-effort mode
if ((ps.mode == graph_oriented_layout_design_params::effort_mode::HIGH_EFFICIENCY &&
(improve_area_solution || improve_wire_solution || improve_crossing_solution ||
improve_acp_solution || improve_custom_solution)) ||
@@ -806,6 +894,10 @@ class graph_oriented_layout_design_impl
* Timeout limit (in ms).
*/
uint64_t timeout;
/**
* Timeout limit reached.
*/
bool timeout_limit_reached = false;
/**
* Start time.
*/
@@ -818,70 +910,66 @@ class graph_oriented_layout_design_impl
* Vector of search space graphs.
*/
std::vector<search_space_graph<ObstrLyt>> ssg_vec;
/**
* Count evaluated paths in the search space graphs.
*/
uint64_t num_evaluated_paths{0ul};
/**
* Keep track of the maximum number of placed nodes.
*/
uint64_t max_placed_nodes{0ul};
std::atomic<uint64_t> max_placed_nodes{0ul};
/**
* The current best solution with respect to area, initialized to the maximum possible value.
* This value will be updated as better solutions are found.
*/
uint64_t best_area_solution = std::numeric_limits<uint64_t>::max();
std::atomic<uint64_t> best_area_solution = std::numeric_limits<uint64_t>::max();
/**
* The current best solution with respect to the number of wire segments, initialized to the maximum possible value.
* This value will be updated as better solutions are found.
*/
uint64_t best_wire_solution = std::numeric_limits<uint64_t>::max();
std::atomic<uint64_t> best_wire_solution = std::numeric_limits<uint64_t>::max();
/**
* The current best solution with respect to the number of crossings, initialized to the maximum possible
* value. This value will be updated as better solutions are found.
*/
uint64_t best_crossing_solution = std::numeric_limits<uint64_t>::max();
std::atomic<uint64_t> best_crossing_solution = std::numeric_limits<uint64_t>::max();
/**
* The current best solution with respect to the area-crossing product (ACP), initialized to the maximum possible
* value. This value will be updated as better solutions are found.
*/
uint64_t best_acp_solution = std::numeric_limits<uint64_t>::max();
std::atomic<uint64_t> best_acp_solution = std::numeric_limits<uint64_t>::max();
/**
* The current best solution with respect to a custom cost objective, initialized to the maximum possible value.
* This value will be updated as better solutions are found.
*/
uint64_t best_custom_solution = std::numeric_limits<uint64_t>::max();
std::atomic<uint64_t> best_custom_solution = std::numeric_limits<uint64_t>::max();
/**
* Current best solution w.r.t. area after relocating POs.
*/
uint64_t best_optimized_solution{std::numeric_limits<uint64_t>::max()};
std::atomic<uint64_t> best_optimized_solution{std::numeric_limits<uint64_t>::max()};
/**
* Flag indicating that an initial solution has been found with the layout area as cost objective.
* When set to `true`, subsequent search space graphs with the layout area as cost objective can be pruned.
*/
bool improve_area_solution = false;
std::atomic<bool> improve_area_solution = false;
/**
* Flag indicating that an initial solution has been found with the number of wire segments as cost objective.
* When set to `true`, subsequent search space graphs with the number of wire segments as cost objective can be
* pruned.
*/
bool improve_wire_solution = false;
std::atomic<bool> improve_wire_solution = false;
/**
* Flag indicating that an initial solution has been found with the number of crossings as cost objective.
* When set to `true`, subsequent search space graphs with the number of crossings as cost objective can be pruned.
*/
bool improve_crossing_solution = false;
std::atomic<bool> improve_crossing_solution = false;
/**
* Flag indicating that an initial solution has been found with the area-crossings product as cost objective.
* When set to `true`, subsequent search space graphs with the area-crossing product as cost objective can be
* pruned.
*/
bool improve_acp_solution = false;
std::atomic<bool> improve_acp_solution = false;
/**
* Flag indicating that an initial solution has been found with a custom cost objective.
* When set to `true`, subsequent search space graphs with a custom cost objective can be pruned.
*/
bool improve_custom_solution = false;
std::atomic<bool> improve_custom_solution = false;
/**
* In high-efficiency mode, only 2 search space graphs are used
*/
@@ -1451,7 +1539,10 @@ class graph_oriented_layout_design_impl

// check if solution was found and update max_placed_nodes
const auto found_solution = (place_info.current_node == ssg.nodes_to_place.size());
max_placed_nodes = std::max(place_info.current_node, max_placed_nodes);
if (place_info.current_node > max_placed_nodes)
{
max_placed_nodes = place_info.current_node;
}

return found_solution;
}
@@ -1476,7 +1567,6 @@ class graph_oriented_layout_design_impl

// output the elapsed time
std::cout << fmt::format("[i] Time taken: {} s {} ms {} µs\n", sec, ms, us);
std::cout << fmt::format("[i] Evaluated paths: {}\n", num_evaluated_paths);
std::cout << fmt::format("[i] Layout dimension: {} × {} = {}\n", lyt.x() + 1, lyt.y() + 1, lyt.area());
std::cout << fmt::format("[i] #Wires: {}\n", lyt.num_wires() - lyt.num_pis() - lyt.num_pos());
std::cout << fmt::format("[i] #Crossings: {}\n", lyt.num_crossings());
@@ -1639,6 +1729,8 @@ class graph_oriented_layout_design_impl
uint64_t cost = 0ul;
uint64_t desired_cost = 0ul;

if (found_solution)
{}
bool improve_solution = improve_custom_solution;
uint64_t best_solution = best_custom_solution;

Loading