1 #ifndef LOAD_BALANCE_HPP
2 #define LOAD_BALANCE_HPP
14 extern "C" void set_weights(
int* gvid0_pid,
double* ptl_count,
double* f0_node_cost);
37 MPI_Comm inter_period_comm;
48 void update_model_no_history(
const View<int*,CLayout,HostType>& current_partition,
const View<double*, HostType>& all_periods_timings){
50 for(
int i=0; i<all_periods_timings.size(); i++){
51 int node_offset = current_partition(i) - 1;
52 int next_node_offset = current_partition(i+1) - 1;
53 int nnodes = next_node_offset - node_offset;
55 double time_per_node = all_periods_timings(i)/nnodes;
56 for(
int j=0; j<nnodes; j++){
62 void update_model_exp_history(
const View<int*,CLayout,HostType>& current_partition,
const View<double*, HostType>& all_periods_timings){
64 for(
int i=0; i<all_periods_timings.size(); i++){
66 int node_offset = current_partition(i) - 1;
67 int next_node_offset = current_partition(i+1) - 1;
68 int nnodes = next_node_offset - node_offset;
71 double avg_time_per_node = all_periods_timings(i)/nnodes;
74 double expected_time = 0.0;
75 for(
int j=0; j<nnodes; j++){
78 double expected_time_per_node = expected_time/nnodes;
81 double extra_time_per_node = avg_time_per_node - expected_time_per_node;
83 for(
int j=0; j<nnodes; j++){
85 constexpr
double adjustment_rate = 0.5;
94 double new_accumulated_time = 0.0;
98 double accumulated_single_timer = 0;
99 int ierr = GPTLget_wallclock(
timer_names[i].c_str(), -1, &accumulated_single_timer);
102 if(ierr != 0) accumulated_single_timer = 0.0;
104 new_accumulated_time += accumulated_single_timer;
111 time_accumulated = new_accumulated_time;
130 inter_period_comm(inter_period_comm),
131 period_comm(period_comm),
134 update_method(update_method),
136 region_name(region_name),
137 timer_names(timer_names)
148 MPI_Comm_size(inter_period_comm, &
n_periods);
183 double largest_time = 0.0;
185 for(
int i=0; i<(proposed_partition.size()-1); i++){
186 double proc_time = 0.0;
187 for(
int i_node=proposed_partition(i)-1; i_node<proposed_partition(i+1)-1; i_node++){
190 if(proc_time>largest_time){
192 largest_time = proc_time;
205 void update_model(
const View<int*,CLayout,HostType>& current_partition,
double manual_time=-1.0){
221 MPI_Reduce(&time_spent_this_rank, &time_spent, 1, MPI_DOUBLE, MPI_MAX, 0, inter_period_comm);
223 time_spent = time_spent_this_rank;
227 MPI_Gather(&time_spent, 1, MPI_DOUBLE, all_periods_timings.data(), 1, MPI_DOUBLE, 0, period_comm);
237 double observed_sum_region_time = 0.0;
238 for(
int i=0; i<all_periods_timings.size(); i++){
240 observed_sum_region_time += all_periods_timings(i);
265 exit_XGC(
"Error: Update method not available\n");
307 Kokkos::parallel_reduce(
"sum", Kokkos::RangePolicy<HostExSpace>(0,input.size()), [=](
const int i,
double& l_total){
315 bool greedily_fill_partition(
const View<double*,HostType>& weight,
const View<double*,HostType>& constraint1,
double target_weight_per_rank){
316 int nnode = weight.size();
320 double constraint1_in_this_rank = 0.0;
321 double weight_in_this_rank = 0.0;
324 bool assign_one_node_per_proc =
false;
325 for(
int i_node = 0; i_node<nnode; i_node++){
328 if(nnode-i_node==nproc-pid) assign_one_node_per_proc =
true;
331 bool rank_is_loaded =
false;
334 if(assign_one_node_per_proc) rank_is_loaded =
true;
340 if(weight_in_this_rank>=target_weight_per_rank) rank_is_loaded =
true;
346 constraint1_in_this_rank = 0.0;
347 weight_in_this_rank = 0.0;
349 if(pid==nproc-1)
break;
351 constraint1_in_this_rank += constraint1(i_node);
352 weight_in_this_rank += weight(i_node);
359 constraint1_in_this_rank = 0.0;
361 constraint1_in_this_rank += constraint1(i_node);
373 double largest_time = 0.0;
375 for(
int i=0; i<(partition.size()-1); i++){
376 double proc_time = 0.0;
377 for(
int i_node=partition(i)-1; i_node<partition(i+1)-1; i_node++){
378 proc_time += weight(i_node);
380 if(proc_time>largest_time){
382 largest_time = proc_time;
388 void one_weight_balance(
const View<double*,HostType>& weight,
const View<double*,CLayout,HostType> constraint1){
397 if(!meets_constraints){
401 if(!meets_constraints)
exit_XGC(
"\nUnexpected issue in load balance: constraint-based partition doesn't satisfy constraints\n");
405 if(
verbose) printf(
"The ideal distribution (%1.3e) does not satisfy constraints. This distribution (%1.3e) does.\n", ideal_weight_per_rank, upper_limit_weight_per_rank);
409 const double desired_precision = 0.01;
410 double desired_step_size = ideal_weight_per_rank*desired_precision;
411 double step_size = upper_limit_weight_per_rank - ideal_weight_per_rank;
412 double compromise_weight_per_rank = upper_limit_weight_per_rank;
413 if(
verbose) printf(
"\nEmploying a binary search to narrow down on the best option.");
414 while(step_size>desired_step_size){
418 if(meets_constraints){
420 compromise_weight_per_rank -= step_size;
423 compromise_weight_per_rank += step_size;
428 if(
verbose) printf(
"\n Stepped by %1.3e to %1.3e. The new partition does%s meet constraints.", step_size, compromise_weight_per_rank, meets_constraints?
"" :
"nt");
431 while(!meets_constraints){
432 compromise_weight_per_rank += step_size;
434 if(
verbose) printf(
"\n Stepped by %1.3e UP to %1.3e. The new partition does%s meet constraints.", step_size, compromise_weight_per_rank, meets_constraints?
"" :
"nt");
451 double observed_max_all_rgns_time = 0.0;
452 for(
int i=0; i<
regions.size(); i++){
453 observed_max_all_rgns_time +=
regions[i].get_observed_max_region_time();
457 double predicted_max_all_rgns_time = 0.0;
458 for(
int i=0; i<
regions.size(); i++){
462 double fractional_improvement = 1.0 - predicted_max_all_rgns_time/observed_max_all_rgns_time;
465 printf(
"\nDetermining whether to adopt the proposed partition:");
466 printf(
"\n Observed time with current partition was: %1.3e", observed_max_all_rgns_time);
467 printf(
"\n Predicted time with proposed partition, adjusting for historical undershoot (of Rgn 0: %1.3e): %1.3e",
regions[0].get_prediction_undershoot(), predicted_max_all_rgns_time);
468 printf(
"\n Adopted if Fractional improvement of adjusted prediction (%1.3e) exceeds specified threshold (%1.3e)\n", fractional_improvement,
threshold_to_rebalance);
487 void update_model(
const View<int*,CLayout,HostType>& current_partition){
488 for(
int i=0; i<
regions.size(); i++){
489 regions[i].update_model(current_partition);
494 void update_model(
const View<int*,CLayout,HostType>& current_partition,
const std::vector<double>& manual_times){
495 for(
int i=0; i<
regions.size(); i++){
496 regions[i].update_model(current_partition, manual_times[i]);
501 for(
int i=0; i<
regions.size(); i++){
507 bool is_initialized=
true;
508 for(
int i=0; i<
regions.size(); i++){
509 is_initialized = (is_initialized &&
regions[i].get_model_is_initialized());
511 return is_initialized;
515 auto constraint1 = ptl_count;
517 if(
regions.size()!=1)
exit_XGC(
"Error: Load balancing is currently single-region only\n");
524 printf(
"\nNewly PROPOSED partition:\n");
554 printf(
"\nNEW PARTITION:\n");
565 const View<int*,CLayout,HostType>& old_partition){
568 TIMER(
"F0_REDISTRIBUTE",
569 f0_redistribute(plasma, pol_decomp, grid, magnetic_field, vgrid, old_partition) );
592 return (should_rebalance_int==1);
595 bool should_rebalance;
605 int should_rebalance_int = (should_rebalance ? 1 : 0);
607 MPI_Bcast(&should_rebalance_int, 1, MPI_INT, 0, comm);
609 return (should_rebalance_int==1);
620 , comm(sync_planes ? pol_decomp.mpi.comm : pol_decomp.mpi.plane_comm)
623 double max_mem_redist_gb = 10.0;
624 std::string weighting_algorithm_str =
"Fortran";
625 std::string update_method_str =
"NoHistory";
628 max_mem_redist_gb = nlr.
get<
double>(
"max_mem_redist_gb", 10.0);
629 weighting_algorithm_str = nlr.
get<std::string>(
"weighting_algorithm",
"Fortran");
632 update_method_str = nlr.
get<std::string>(
"update_method",
"NoHistory");
639 double max_n_ptl_on_rank = max_mem_redist_gb*1024*1024*1024/80.0;
641 if(weighting_algorithm_str==
"Fortran"){
643 }
else if(weighting_algorithm_str==
"ParticleBalance"){
645 }
else if(weighting_algorithm_str==
"SingleRegionBalance"){
648 exit_XGC(
"\nError: weighting_algorithm input not valid.");
653 MPI_Comm inter_period_comm = sync_planes ? pol_decomp.mpi.intpl_comm : MPI_COMM_SELF;
658 if(update_method_str==
"NoHistory"){
660 }
else if(update_method_str==
"ExpHistory"){
663 exit_XGC(
"\nError: update_method input not valid.");
680 MPI_Comm_size(inter_period_comm, &n_periods);
683 exit_XGC(
"\nError: ParticleConstraint is only available ConstraintOption.\n");
700 if(weighting_algorithm == WeightingAlgorithm::Default){
701 weighting_algorithm = default_weighting_algorithm;
704 if(default_weighting_algorithm==WeightingAlgorithm::Fortran){
706 weighting_algorithm = default_weighting_algorithm;
710 if(weighting_algorithm==WeightingAlgorithm::Fortran) printf(
"\nLoad balance called with Fortran algorithm\n");
711 else if(weighting_algorithm==WeightingAlgorithm::ParticleBalance) printf(
"\nLoad balance called with Ptl algorithm\n");
712 else if(weighting_algorithm==WeightingAlgorithm::SingleRegionBalance) printf(
"\nLoad balance called with SingleRegion algorithm\n");
716 if(weighting_algorithm!=WeightingAlgorithm::Fortran){
721 double avg_n_ptl = (double)(total_n_ptl)/pol_decomp.mpi.nranks;
722 double max_ratio = max_n_ptl/avg_n_ptl;
723 if(
is_rank_zero()) printf(
" Species %d: (max/avg ptl per rank = %1.2f); total n_ptl = %lld\n", species.
idx, max_ratio, total_n_ptl);
731 if(weighting_algorithm!=WeightingAlgorithm::ParticleBalance){
732 if(model_is_initialized()){
734 TIMER(
"LOAD_BAL_UPDATE",
737 if(
is_rank_zero() && verbose) printf(
"Initializing timing model, no partition proposal yet.");
746 if (
is_rank_zero()) propose_new_partition(ptl_count, weighting_algorithm);
752 if(will_rebalance(reweight_option, weighting_algorithm, f0_cost)){
754 View<int*,CLayout,HostType> old_partition(
NoInit(
"old_partition"), pol_decomp.
gvid0_pid_h.layout());
755 Kokkos::deep_copy(old_partition, pol_decomp.
gvid0_pid_h);
758 TIMER(
"LOAD_BAL_SET_NEW",
759 set_new_partition(sml, grid, magnetic_field, vgrid, plasma, pol_decomp, weighting_algorithm) );
762 TIMER(
"LOAD_BAL_REDIST",
763 redistribute_load(sml, grid, magnetic_field, vgrid, plasma, pol_decomp, old_partition) );
776 load_imbalance = regions[0].get_observed_load_imbalance();
777 model_belief = regions[0].get_estimated_time_per_vertex();
780 if (
is_rank_zero()) propose_new_partition(constraint, default_weighting_algorithm);
782 double f0_cost = 0.0;
783 if(will_rebalance(ReweightOption::IfBetter, default_weighting_algorithm, f0_cost)){
785 MPI_Bcast(proposed_partition.data(), proposed_partition.size(), MPI_INT, 0, comm);
788 Kokkos::deep_copy(pol_decomp.
gvid0_pid_h, proposed_partition);
bool model_has_history
Definition: load_balance.hpp:30
View< double *, HostType > estimated_time_per_vertex
Definition: load_balance.hpp:25
static int GPTLstart(const char *name)
Definition: timer_macro.hpp:9
bool will_rebalance(ReweightOption reweight_option, WeightingAlgorithm weighting_algorithm, double f0_cost)
Definition: load_balance.hpp:577
bool is_rank_zero()
Definition: globals.hpp:27
void one_weight_balance(const View< double *, HostType > &weight, const View< double *, CLayout, HostType > constraint1)
Definition: load_balance.hpp:388
double threshold_to_rebalance
Definition: load_balance.hpp:300
T get(const string ¶m, const T default_val, int val_ind=0)
Definition: NamelistReader.hpp:373
double time_accumulated
Definition: load_balance.hpp:44
bool f0_grid() const
Definition: plasma.hpp:202
double predicted_max_region_time
Definition: load_balance.hpp:31
double observed_max_region_time
Definition: load_balance.hpp:32
double get_largest_predicted_time(const View< int *, CLayout, HostType > &partition, const View< double *, HostType > &weight) const
Definition: load_balance.hpp:372
Definition: velocity_grid.hpp:8
void update_model_exp_history(const View< int *, CLayout, HostType > ¤t_partition, const View< double *, HostType > &all_periods_timings)
Definition: load_balance.hpp:62
int n_unique_ranks
How many ranks are in a 'period' (in tokamaks, in a plane)
Definition: load_balance.hpp:41
LoadBalance(NLReader::NamelistReader &nlr, const DomainDecomposition< DeviceType > &pol_decomp, bool sync_planes=true)
Definition: load_balance.hpp:617
subroutine plasma(grid, itr, p, dene_out, deni_out, Te_out, Ti_out, Vparai_out)
Calculate the plasma density, temperature, and parallel velocity for a point in triangle itr using pl...
Definition: neutral_totalf.F90:1224
void rebalance(DomainDecomposition< DeviceType > &pol_decomp, const View< double *, CLayout, HostType > &constraint, const std::vector< double > &timings, double &load_imbalance, View< double *, HostType > &model_belief)
Definition: load_balance.hpp:772
Definition: plasma.hpp:107
Definition: NamelistReader.hpp:193
Definition: magnetic_field.hpp:12
void propose_new_partition(const Kokkos::View< double *, Kokkos::LayoutRight, HostType > &ptl_count, WeightingAlgorithm weighting_algorithm)
Definition: load_balance.hpp:514
int idx
Index in all_species.
Definition: species.hpp:78
bool verbose
Definition: load_balance.hpp:301
void for_all_nonadiabatic_species(F func, DevicePtlOpt device_ptl_opt=UseDevicePtl)
Definition: plasma.hpp:126
double prediction_undershoot
Definition: load_balance.hpp:34
double get_even_division(const View< double *, HostType > &input, int n) const
Definition: load_balance.hpp:305
Kokkos::View< double **, Kokkos::LayoutRight, HostType > count_ptl_per_node_elec_main_ion(const Grid< DeviceType > &grid, const MagneticField< DeviceType > &magnetic_field, Plasma &plasma, bool kinetic_electrons)
Definition: count_ptl_per_node.cpp:29
double observed_load_imbalance
Definition: load_balance.hpp:33
void update_pol_decomp()
Definition: domain_decomposition.tpp:213
void update_model(const View< int *, CLayout, HostType > ¤t_partition)
Definition: load_balance.hpp:487
long long int get_total_n_ptl()
Definition: species.hpp:693
void f0_redistribute(Plasma &plasma, const DomainDecomposition< DeviceType > &pol_decomp, const Grid< DeviceType > &grid, const MagneticField< DeviceType > &magnetic_field, const VelocityGrid &vgrid, const View< int *, CLayout, HostType > &old_partition)
Definition: f0_redistribute.cpp:184
void set_new_partition(const Simulation< DeviceType > &sml, const Grid< DeviceType > &grid, const MagneticField< DeviceType > &magnetic_field, const VelocityGrid &vgrid, Plasma &plasma, DomainDecomposition< DeviceType > &pol_decomp, WeightingAlgorithm weighting_algorithm)
Definition: load_balance.hpp:529
#define TIMER(N, F)
Definition: timer_macro.hpp:24
void rebalance(const Simulation< DeviceType > &sml, const Grid< DeviceType > &grid, const MagneticField< DeviceType > &magnetic_field, const VelocityGrid &vgrid, Plasma &plasma, DomainDecomposition< DeviceType > &pol_decomp, ReweightOption reweight_option, WeightingAlgorithm weighting_algorithm=WeightingAlgorithm::Default)
Definition: load_balance.hpp:688
void use_namelist(const string &namelist)
Definition: NamelistReader.hpp:355
ConstraintOption
Definition: load_balance.hpp:279
void update_model(const View< int *, CLayout, HostType > ¤t_partition, const std::vector< double > &manual_times)
Definition: load_balance.hpp:494
double get_estimated_time_per_vertex(int i) const
Definition: load_balance.hpp:157
int my_period_rank
Definition: load_balance.hpp:42
int nnodes_on_plane
Number of nodes on local plane.
Definition: domain_decomposition.hpp:53
void calculate_load_imbalance(double f0_cost)
void initialize_model()
Definition: load_balance.hpp:198
WeightingAlgorithm default_weighting_algorithm
Definition: load_balance.hpp:290
View< int *, CLayout, HostType > proposed_partition
Which processors get which vertices.
Definition: load_balance.hpp:294
double get_time_since_previous_call()
Definition: load_balance.hpp:92
bool get_model_is_initialized() const
Definition: load_balance.hpp:177
UpdateMethod
Definition: load_balance.hpp:18
std::string region_name
Definition: load_balance.hpp:26
bool verbose
Definition: load_balance.hpp:28
std::vector< LoadRegion > regions
Definition: load_balance.hpp:292
bool namelist_present(const string &namelist)
Definition: NamelistReader.hpp:351
UpdateMethod update_method
Definition: load_balance.hpp:46
bool model_is_initialized
Definition: load_balance.hpp:29
bool electron_on
Use kinetic electrons.
Definition: sml.hpp:63
void reset_cost_trackers()
void redistribute_load(const Simulation< DeviceType > &sml, const Grid< DeviceType > &grid, const MagneticField< DeviceType > &magnetic_field, const VelocityGrid &vgrid, Plasma &plasma, DomainDecomposition< DeviceType > &pol_decomp, const View< int *, CLayout, HostType > &old_partition)
Definition: load_balance.hpp:563
double get_largest_predicted_time(const View< int *, CLayout, HostType > &proposed_partition) const
Definition: load_balance.hpp:182
void initialize_model()
Definition: load_balance.hpp:500
T::value_type sum_view(const T &view)
Definition: view_arithmetic.hpp:87
bool recommend_proposed_partition()
Definition: load_balance.hpp:449
void exit_XGC(std::string msg)
Definition: globals.hpp:37
bool greedily_fill_partition(const View< double *, HostType > &weight, const View< double *, HostType > &constraint1, double target_weight_per_rank)
Definition: load_balance.hpp:315
int n_periods
How many repeating periods there are; in tokamaks this is planes.
Definition: load_balance.hpp:40
Definition: magnetic_field.F90:1
Definition: load_balance.hpp:16
void print_new_partition()
Definition: load_balance.hpp:479
WeightingAlgorithm
Definition: load_balance.hpp:272
int assess_whether_to_rebalance_load()
View< double *, HostType > get_estimated_time_per_vertex() const
Definition: load_balance.hpp:161
Definition: plasma.hpp:13
double constraint1_max
Definition: load_balance.hpp:303
void set_weights(int *gvid0_pid, double *ptl_count, double *f0_node_cost)
View< double *, CLayout, HostType > count_all_ptl_per_node(const Grid< DeviceType > &grid, const MagneticField< DeviceType > &magnetic_field, Plasma &plasma)
Definition: count_ptl_per_node.cpp:71
ReweightOption
Definition: load_balance.hpp:283
View< double *, CLayout, HostType > f0_node_cost
Definition: plasma.hpp:36
void shift_all_species(Plasma &plasma, const Grid< DeviceType > &grid, const MagneticField< DeviceType > &magnetic_field, const DomainDecomposition< DeviceType > &pol_decomp, Shift::ShiftPh0 shift_ph0)
Definition: shift.cpp:301
void reset_timer()
Definition: load_balance.hpp:152
Definition: species.hpp:75
void update_model(const View< int *, CLayout, HostType > ¤t_partition, double manual_time=-1.0)
Definition: load_balance.hpp:205
bool model_is_initialized()
Definition: load_balance.hpp:506
std::vector< std::string > timer_names
Definition: load_balance.hpp:27
bool pol_decomp
Use poloidal decomposition.
Definition: domain_decomposition.hpp:46
double get_observed_max_region_time() const
Definition: load_balance.hpp:169
Definition: load_balance.hpp:270
void touch_timers()
Definition: load_balance.hpp:116
Kokkos::ViewAllocateWithoutInitializing NoInit
Definition: space_settings.hpp:68
void update_model_no_history(const View< int *, CLayout, HostType > ¤t_partition, const View< double *, HostType > &all_periods_timings)
Definition: load_balance.hpp:48
double get_prediction_undershoot() const
Definition: load_balance.hpp:165
double get_observed_load_imbalance() const
Definition: load_balance.hpp:173
int get_max_n_ptl()
Definition: species.hpp:704
static int GPTLstop(const char *name)
Definition: timer_macro.hpp:10
Kokkos::View< int *, Kokkos::LayoutRight, HostType > gvid0_pid_h
Which processors get which vertices (host)
Definition: domain_decomposition.hpp:63