Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1f2a323
Add commit records and evaluation cancellation support to NetCostHandler
soheilshahrouz Jul 30, 2026
4b476ba
Add connection commit records and a move-reload helper to placement s…
soheilshahrouz Jul 30, 2026
c0e7128
Add MoveGenerator::sync_state_from to copy RL agent state
soheilshahrouz Jul 30, 2026
aeed471
Factor swap evaluation/commit/revert into SwapEvaluator
soheilshahrouz Jul 30, 2026
db5e765
Add --place_parallel_eval option
soheilshahrouz Jul 30, 2026
c5f8066
Add speculative parallel swap evaluation engine
soheilshahrouz Jul 30, 2026
70f3330
Use SwapEvaluator in annealer and add parallel inner loop
soheilshahrouz Jul 30, 2026
a4004e1
Rename --place_parallel_eval to --place_swap_eval_num_workers
soheilshahrouz Jul 30, 2026
7cb5ab7
Rename place_swap_eval_num_workers to swap_eval_num_workers
soheilshahrouz Jul 30, 2026
0915168
Rename t_evaluated_move to t_swap_cost_deltas
soheilshahrouz Jul 30, 2026
2acc55a
// instead of /* */ in swap_evaluator.cpp
soheilshahrouz Jul 30, 2026
006829e
Updated comments in swap_evaluator.cpp
soheilshahrouz Jul 30, 2026
b767f02
Trim placer comments and spell out deduced types
soheilshahrouz Jul 30, 2026
57c25ad
Rename eval to deltas and simplify parallel engine docs
soheilshahrouz Jul 30, 2026
427cbbb
Rename sync_replicas and num_retired_attempts, clarify engine docs
soheilshahrouz Jul 31, 2026
85827ba
Drop the divergence tripwire and make accept_rand a local
soheilshahrouz Jul 31, 2026
c5319c3
Reuse attempt slots and commit record buffers across batches
soheilshahrouz Jul 31, 2026
f48e3f0
Make the coordinator evaluate worker 0's share instead of spinning
soheilshahrouz Jul 31, 2026
27b84a6
Overlap replica winner commits with the annealer's batch bookkeeping
soheilshahrouz Jul 31, 2026
770a80e
Cache-line align speculative swap slots to avoid false sharing
soheilshahrouz Jul 31, 2026
5ef7700
Use a fixed speculative window equal to the worker count.
soheilshahrouz Jul 31, 2026
2274339
Reset attempt slots on the evaluating workers instead of the coordinator
soheilshahrouz Jul 31, 2026
4c92a25
Replace the swap cancellation callback with a token and poll inside t…
soheilshahrouz Jul 31, 2026
9d0280b
Put the engine's contended atomics on separate cache lines.
soheilshahrouz Jul 31, 2026
fe3317b
Merge branch 'master' into par_spec_sa
soheilshahrouz Aug 2, 2026
47560ca
Merge remote-tracking branch 'origin/master' into par_spec_sa
soheilshahrouz Aug 2, 2026
e99fa7d
Correct swap_eval_num_workers's comment
soheilshahrouz Aug 2, 2026
5444eda
Clean up try_swap_ comments and named bool args
soheilshahrouz Aug 2, 2026
e023c68
Inline init_parallel_engine_ into its only caller
soheilshahrouz Aug 2, 2026
f882f99
Use a fixed batch size in the parallel inner loop
soheilshahrouz Aug 2, 2026
5de534d
Trim comments in the parallel inner loop
soheilshahrouz Aug 2, 2026
be88a5f
make format
soheilshahrouz Aug 2, 2026
2f0c719
Merge branch 'master' into par_spec_sa
soheilshahrouz Aug 11, 2026
2871848
Merge branch 'master' into par_spec_sa
soheilshahrouz Aug 26, 2026
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
12 changes: 12 additions & 0 deletions vpr/src/base/read_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2647,6 +2647,18 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio
.default_value("0")
.show_in(argparse::ShowIn::HELP_ONLY);

place_grp.add_argument(args.place_swap_eval_num_workers, "--place_swap_eval_num_workers")
.help(
"Number of workers used to speculatively evaluate placement swaps in "
"parallel during simulated annealing. The annealing thread itself acts as one "
"of the workers:\n"
" * 1 (the default) uses the sequential annealer,\n"
" * >1 sets the number of workers, and\n"
" * 0 uses the maximum concurrency supported by the host machine.\n"
"Results are deterministic for a fixed value of this option.")
.default_value("1")
.show_in(argparse::ShowIn::HELP_ONLY);

place_grp.add_argument(args.place_static_move_prob, "--place_static_move_prob")
.help(
"The percentage probabilities of different moves in Simulated Annealing placement. "
Expand Down
1 change: 1 addition & 0 deletions vpr/src/base/read_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ struct t_options {
argparse::ArgValue<float> place_rlim_escape_fraction;
argparse::ArgValue<std::string> place_move_stats_file;
argparse::ArgValue<int> placement_saves_per_temperature;
argparse::ArgValue<int> place_swap_eval_num_workers;
argparse::ArgValue<e_place_effort_scaling> place_effort_scaling;
argparse::ArgValue<e_place_delta_delay_algorithm> place_delta_delay_matrix_calculation_method;
argparse::ArgValue<std::vector<float>> place_static_move_prob;
Expand Down
8 changes: 8 additions & 0 deletions vpr/src/base/setup_vpr.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <vector>
#include <list>
#include <thread>

#include "setup_vpr.h"
#include "physical_types_util.h"
Expand Down Expand Up @@ -713,6 +714,13 @@ static void setup_placer_opts(const t_options& Options, t_placer_opts* PlacerOpt
PlacerOpts->rlim_escape_fraction = Options.place_rlim_escape_fraction;
PlacerOpts->move_stats_file = Options.place_move_stats_file;
PlacerOpts->placement_saves_per_temperature = Options.placement_saves_per_temperature;
// 0 requests the host's maximum concurrency; resolve it here so the placer
// always sees a concrete worker count (>= 1, where 1 means sequential).
PlacerOpts->swap_eval_num_workers = Options.place_swap_eval_num_workers;
if (PlacerOpts->swap_eval_num_workers <= 0) {
const unsigned int hw_concurrency = std::thread::hardware_concurrency();
PlacerOpts->swap_eval_num_workers = (hw_concurrency > 0) ? (int)hw_concurrency : 1;
}
PlacerOpts->place_delta_delay_matrix_calculation_method = Options.place_delta_delay_matrix_calculation_method;

PlacerOpts->strict_checks = Options.strict_checks;
Expand Down
6 changes: 6 additions & 0 deletions vpr/src/base/vpr_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,12 @@ struct t_placer_opts {

int placement_saves_per_temperature;

/// Number of workers used to speculatively evaluate placement swaps in parallel
/// (1 = sequential annealer). The annealing thread is worker 0, so W workers
/// spawn W-1 threads. Always >= 1 here; 0 is resolved to the host concurrency
/// during setup.
int swap_eval_num_workers;

e_place_effort_scaling effort_scaling;

e_timing_update_type timing_update_type;
Expand Down
304 changes: 221 additions & 83 deletions vpr/src/place/annealer.cpp

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions vpr/src/place/annealer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
#include "move_generator.h" // movestats
#include "net_cost_handler.h"
#include "manual_move_generator.h"
#include "swap_evaluator.h"
#include "vtr_random.h"

#include <memory>
#include <optional>
#include <tuple>

Expand All @@ -19,6 +21,7 @@ enum class e_agent_state;
class NocCostHandler;
class InterposerCostHandler;
class NetPinTimingInvalidator;
class ParallelAnnealEngine;
class PlacerSetupSlacks;

/**
Expand Down Expand Up @@ -198,6 +201,8 @@ class PlacementAnnealer {
float auto_init_t_scale,
int move_lim);

~PlacementAnnealer();

/**
* @brief Contains the inner loop of the simulated annealing that performs
* a certain number of swaps with a single temperature
Expand Down Expand Up @@ -287,6 +292,26 @@ class PlacementAnnealer {
/// results from a set of trial swaps.
float estimate_starting_temp_using_cost_variance_();

/**
* @brief Returns true when the inner loop should run with speculative
* parallel swap evaluation.
*
* Requires --place_swap_eval_num_workers > 1 and a configuration the parallel
* engine supports (CRITICALITY_TIMING_PLACE or BOUNDING_BOX_PLACE, no NoC
* optimization, no per-move logging/saving, no graphics). Unsupported
* configurations fall back to the sequential inner loop with a one-time
* warning.
*/
bool should_use_parallel_inner_loop_();

/**
* @brief Parallel counterpart of placement_inner_loop().
*
* Runs the inner loop as a sequence of speculative batches,
* each issuing one attempt per evaluator.
*/
void placement_inner_loop_parallel_();

private:
const t_placer_opts& placer_opts_;
PlacerState& placer_state_;
Expand Down Expand Up @@ -330,6 +355,13 @@ class PlacementAnnealer {
/// Keep record of moved blocks and affected pins in a swap
t_pl_blocks_to_be_moved blocks_affected_;

/// Evaluates/commits/reverts swaps
std::unique_ptr<SwapEvaluator> swap_evaluator_;
/// Speculative parallel swap evaluation engine.
std::unique_ptr<ParallelAnnealEngine> parallel_engine_;
/// Ensures the fallback-to-sequential warning is only printed once
bool parallel_fallback_warned_ = false;

private:
/**
* @brief The maximum number of swap attempts before invoking the
Expand Down
19 changes: 19 additions & 0 deletions vpr/src/place/move_generators/move_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,25 @@ class MoveGenerator {
*/
virtual void process_outcome(double /*reward*/, e_reward_function /*reward_fun*/) {}

/**
* @brief Returns an identifier for the internal decision ("arm") behind the
* most recent propose_move(). Only meaningful for RL-agent based generators.
* Capturing it after each proposal and restoring it before the matching
* outcome lets a caller interleave proposals and still replay outcomes in order.
*/
virtual size_t get_last_action() const { return 0; }

/// @brief Restores the action identifier captured by get_last_action(). See get_last_action().
virtual void set_last_action(size_t /*action*/) {}

/**
* @brief Copies the RL agent's Q-values from `other`, which must be
* a generator of the same concrete type, so a replica proposes exactly
* what the master would. The default is a no-op: most generators depend
* only on the placement state and the RNG.
*/
virtual void sync_state_from(const MoveGenerator& /*other*/) {}

/**
* @brief Calculates the agent's reward and the total process outcome
*
Expand Down
15 changes: 15 additions & 0 deletions vpr/src/place/move_generators/simpleRL_move_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ void SimpleRLMoveGenerator::process_outcome(double reward, e_reward_function rew
karmed_bandit_agent->process_outcome(reward, reward_fun);
}

void SimpleRLMoveGenerator::sync_state_from(const MoveGenerator& other) {
const auto* other_rl = dynamic_cast<const SimpleRLMoveGenerator*>(&other);
VTR_ASSERT_MSG(other_rl != nullptr, "Can only sync agent state from another SimpleRLMoveGenerator.");
karmed_bandit_agent->copy_state_from(*other_rl->karmed_bandit_agent);
}

/* *
* *
* K-Armed bandit agent implementation *
Expand Down Expand Up @@ -164,6 +170,15 @@ void KArmedBanditAgent::write_agent_info(int last_action, double reward) {
fflush(agent_info_file_);
}

void KArmedBanditAgent::copy_state_from(const KArmedBanditAgent& other) {
VTR_ASSERT_SAFE(num_available_actions_ == other.num_available_actions_);
VTR_ASSERT_SAFE(q_.size() == other.q_.size());

exp_alpha_ = other.exp_alpha_;
q_ = other.q_;
num_action_chosen_ = other.num_action_chosen_;
}

void KArmedBanditAgent::set_step(float gamma, int move_lim) {
if (gamma < 0) {
exp_alpha_ = -1; //Use sample average
Expand Down
28 changes: 28 additions & 0 deletions vpr/src/place/move_generators/simpleRL_move_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ class KArmedBanditAgent {
*/
void set_step(float gamma, int move_lim);

/// @brief Returns the action (arm) selected by the most recent propose_action() call.
size_t last_action() const { return last_action_; }

/// @brief Overrides the action credited by the next process_outcome() call.
/// See MoveGenerator::get_last_action() for the intended usage.
void set_last_action(size_t action) { last_action_ = action; }

/**
* @brief Copies the evolving state (Q-values, action counts, step size) from
* `other`, making this agent propose exactly what `other` would.
*
* The derived agents' remaining members are either configuration fixed at
* construction or scratch recomputed from q_ on every propose_action() call,
* so copying the base state is sufficient. Both agents must have been
* constructed with identical configurations.
* See MoveGenerator::sync_state_from() for the intended usage.
*/
void copy_state_from(const KArmedBanditAgent& other);

protected:
/**
* @brief Converts an action index to a move type.
Expand Down Expand Up @@ -252,6 +271,15 @@ class SimpleRLMoveGenerator : public MoveGenerator {

// Receives feedback about the outcome of the previously proposed move
void process_outcome(double reward, e_reward_function reward_fun) override;

// Returns/overrides the agent action behind the most recent proposal.
// See MoveGenerator::get_last_action() for the intended usage.
size_t get_last_action() const override { return karmed_bandit_agent->last_action(); }
void set_last_action(size_t action) override { karmed_bandit_agent->set_last_action(action); }

// Copies the agent state from another SimpleRLMoveGenerator.
// See MoveGenerator::sync_state_from() for the intended usage.
void sync_state_from(const MoveGenerator& other) override;
};

template<class T, class>
Expand Down
10 changes: 10 additions & 0 deletions vpr/src/place/move_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ e_block_move_result t_pl_blocks_to_be_moved::record_block_move(ClusterBlockId bl
return e_block_move_result::VALID;
}

void t_pl_blocks_to_be_moved::set_moved_blocks(const std::vector<t_pl_moved_block>& moves) {
VTR_ASSERT_SAFE(moved_blocks.empty() && moved_from.empty() && moved_to.empty() && affected_pins.empty());

moved_blocks = moves;
for (const t_pl_moved_block& moved_block : moved_blocks) {
moved_from.insert(moved_block.old_loc);
moved_to.insert(moved_block.new_loc);
}
}

//Examines the currently proposed move and determine any empty locations
std::set<t_pl_loc> t_pl_blocks_to_be_moved::determine_locations_emptied_by_move() const {
std::set<t_pl_loc> moved_from_set;
Expand Down
8 changes: 8 additions & 0 deletions vpr/src/place/move_transactions.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ struct t_pl_blocks_to_be_moved {
t_pl_loc to,
const BlkLocRegistry& blk_loc_registry);

/**
* @brief Loads a previously recorded move (its full moved blocks list) into
* this object, so the move can be re-applied to another placement
* state. Used by the speculative parallel swap evaluation engine to transfer
* a move proposed on the master state to worker replicas and back.
*/
void set_moved_blocks(const std::vector<t_pl_moved_block>& moves);

std::set<t_pl_loc> determine_locations_emptied_by_move() const;

std::vector<t_pl_moved_block> moved_blocks;
Expand Down
Loading
Loading