Peano
Loading...
Searching...
No Matches
toolbox::loadbalancing::strategies::Hardcoded Class Reference

Hardcoded domain decomposition strategy. More...

#include <Hardcoded.h>

Inheritance diagram for toolbox::loadbalancing::strategies::Hardcoded:
Collaboration diagram for toolbox::loadbalancing::strategies::Hardcoded:

Data Structures

struct  Split
 

Public Member Functions

 Hardcoded (std::initializer_list< int > splittingTrees, std::initializer_list< int > numberOfCells, std::initializer_list< int > destinationRanks, std::initializer_list< peano4::SplitInstruction::Mode > modes)
 Construct hard-coded load balancing strategy.
 
 Hardcoded (std::initializer_list< int > splittingTrees, std::initializer_list< int > numberOfCells, std::initializer_list< int > destinationRanks, peano4::SplitInstruction::Mode mode=peano4::SplitInstruction::Mode::BottomUp)
 Shortcut around other constructor.
 
virtual void finishStep () override
 Finish the current sweep.
 
virtual void finishSimulation () override
 
- Public Member Functions inherited from toolbox::loadbalancing::AbstractLoadBalancing
 AbstractLoadBalancing (Configuration *configuration, CostMetrics *costMetrics)
 Constructor.
 
virtual ~AbstractLoadBalancing ()
 Destructor.
 
virtual std::string toString () const
 Create a string representation of load balancing object.
 
virtual void enable (bool)
 Switch on/off.
 
virtual bool hasSplitRecently () const
 Has mesh split recently.
 
bool isEnabled (bool globally) const
 Is load balancing enabled.
 
virtual int getGlobalNumberOfTrees () const
 Delegate to stats.
 
virtual bool hasStagnated () const
 A load balancing can either be stagnating or be switched off for this predicate to hold.
 
void setConfigurationAndMetricsNullWithoutDelete ()
 This is only used when you concatenate balancing rules and you want to disable any deletion.
 

Private Member Functions

void keepAndValidateLocalSplits ()
 Drop the splits of other ranks and validate the remaining ones.
 
void issueSplits ()
 Hand out the splits that are due.
 

Private Attributes

std::list< Split > _splits
 
bool _hasKeptAndValidatedLocalSplits
 

Static Private Attributes

static tarch::logging::Log _log
 

Additional Inherited Members

- Static Public Attributes inherited from toolbox::loadbalancing::AbstractLoadBalancing
static constexpr int NoHeaviestTreeAvailable = -1
 Is used by tree identification and either indicates that there are no trees at all or means that the heaviest tree is on the blacklist.
 
static constexpr int NumberOfMeshSweepsAfterSplitToMakeMeshConsistentAgain = 3
 Number of mesh sweeps a split needs until the mesh is consistent again.
 
- Protected Member Functions inherited from toolbox::loadbalancing::AbstractLoadBalancing
bool fitsIntoMemory (State state) const
 Ensure enough memory is left-over.
 
bool isInterRankBalancingBad () const
 Is the balancing between the ranks ok.
 
bool isIntraRankBalancingBad () const
 Is the balancing on the rank ok.
 
bool areRanksUnemployed () const
 
double getWeightOfHeaviestLocalSpacetree () const
 
int getIdOfHeaviestLocalSpacetree () const
 Determines the maximum spacetree size a tree should have in the optimal case.
 
int getIdOfHeaviestLocalSpacetree (double tolerance) const
 Similar to getIdOfHeaviestLocalSpacetree() but you might get one of the trees back that is close to the heaviest one up to tolerance.
 
- Protected Attributes inherited from toolbox::loadbalancing::AbstractLoadBalancing
Blacklist _blacklist
 
Statistics _statistics
 
Configuration * _configuration
 
CostMetrics * _costMetrics
 
State _state
 Ensure that you invoke.
 
- Static Protected Attributes inherited from toolbox::loadbalancing::AbstractLoadBalancing
static tarch::logging::Log _log
 

Detailed Description

Hardcoded domain decomposition strategy.

Hardcoded load balancing is useful if you know exactly which domain decomposition you want to build up, or if you have to debug a particular (deterministic) setup. It also comes in handy whenever you can transform your dynamic load balancing challenge into an offline problem. The load balancing's Python API contains all the tools to derive good balancing strategies for offline problems (see Load balancing API).

Enumerated splits

The strategy holds an enumerated list of splits. The \( k \) th split reads

‍Tree splittingTrees[k] hands numberOfCells[k] fine grid cells over to a new tree on rank destinationRanks[k], and it uses modes[k] to select these cells.

The strategy does not say when a split happens. It hands out the splits in the order given, and it hands out the \( k \) th split as soon as

  1. the splitting tree exists on this rank,
  2. the splitting tree holds more than numberOfCells[k] unrefined cells, i.e. it keeps at least one cell after the split, and
  3. Peano accepts the split, i.e. the splitting tree is not busy with another split or join.

If one of these conditions does not hold, the \( k \) th split and all subsequent splits wait for the next mesh sweep. Several splits can go out in the same mesh sweep. Cells that the splitting tree has already promised to other trees in this sweep do not count towards the second condition.

The picture is generated from Hardcoded.dot via

dot -Tpng -Gdpi=110 Hardcoded.dot -o Hardcoded.png
Hardcoded domain decomposition strategy.
Definition Hardcoded.h:189

Peano inverts its traversal in every other mesh sweep, i.e. a split which goes out in an inverted sweep picks its cells from the other end of the space-filling curve. We do not care about this inversion. The new tree still gets the requested number of cells.

As the splits wait until the splitting tree is large enough, they usually fire once the mesh construction has reached the mesh level on which the cells are to be found. For adaptive meshes, a split might hence fire before the mesh construction has terminated.

Split modes

peano4::SplitInstruction::Mode::AggressiveTopDown hands over whole refined cells including all their children, while peano4::SplitInstruction::Mode::BottomUp selects cells on the finest mesh level. Peano applies the mode of the first split that a tree triggers in a mesh sweep to all splits of this tree in this sweep. The strategy therefore never lets one tree issue two splits with different modes in the same mesh sweep. A split with a different mode waits for the next mesh sweep.

Examples

Split a regular mesh of \( 27 \times 27 \) cells into four trees on one rank:

{0, 0, 0}, // splitting trees
{182, 182, 182}, // number of cells
{0, 0, 0}, // destination ranks
);

Tree 0 holds 9 and then 81 cells while the mesh is built up, which is not enough for the first split. Once tree 0 holds all 729 cells, all three splits go out in the same mesh sweep: Tree 0 has 729, 547 and 365 cells left over, respectively, before it hands out the next 182 cells.

If you change the mode of the last split into BottomUp

then the first two splits go out together, while the third split is not allowed to go out in the same sweep as the top-down splits. It goes out one mesh sweep later. As it uses BottomUp, it hands over exactly 182 cells, while the two top-down splits hand over whole refined cells and hence end up with slightly more or fewer cells.

Splits can also continue to split trees that earlier splits have created:

{0, 1}, // split 0 creates tree 1, split 1 splits tree 1
{364, 182},
{0, 0},
);

Here, the second split waits until tree 1 exists and holds more than 182 cells.

MPI

All ranks receive the same configuration. Each rank keeps only the splits whose splitting tree lives on this rank and hands them out in the order given. Splits of trees on other ranks hence never block a rank, and the ranks do not have to synchronise.

To tell which rank hosts a tree, we rely on Peano's tree numbering, which is round robin: peano4::parallel::Node::getId() gives the nth tree on rank r the number n*R+r for R ranks, and peano4::parallel::Node::getRank() maps tree number t onto rank tR. A split instruction cannot say which number the tree that it creates will get, as Peano picks this number. We therefore always assume that the trees are assigned to the ranks round robin, i.e. that the tree which the configuration calls t is created on rank tR. Only then does a later split of tree t in the configuration split the right tree. The Python API (create_constructor_initialisation_string() in toolbox.loadbalancing.api.output.Hardcoded) checks this assumption, as only the offline plan knows which tree each split is meant to create.

The idea behind the hardcoded strategy is that users pipe in a particular configuration tied to their specific choice of ranks. If a hardcoded setup does not use all ranks, there's nothing we can do here. Indeed, there might be cases where this makes sense, if the hardcoded strategy is used in combination with another load balancing scheme. If the strategy is used with fewer ranks than the configuration uses, i.e. if the hardcoded strategy wants to deploy to rank 20 and there are only 10 ranks, then we write an error and continue with a mock up. See Split::validateAndAmend() for details.

Support for files

I originally thought it would be nice to have support for hard-coded load-balancing being encoded in a file. However, this contradicts Peano's philosophy that everything should be in the Python script. Therefore, this strategy does not support file I/O but the load balancing's Python interface does.

Trivia

There's also a hardcoded orchestration strategy in tarch::multicore::orchestration::Hardcoded. This is a totally different notion of being hardcoded: There's the decision-making is hardcoded whereas here the actual decisions are. In this sense, the present name might be chosen inappropriately.

Definition at line 188 of file Hardcoded.h.

Constructor & Destructor Documentation

◆ Hardcoded() [1/2]

toolbox::loadbalancing::strategies::Hardcoded::Hardcoded ( std::initializer_list< int >  splittingTrees,
std::initializer_list< int >  numberOfCells,
std::initializer_list< int >  destinationRanks,
std::initializer_list< peano4::SplitInstruction::Mode >  modes 
)

Construct hard-coded load balancing strategy.

The splits are passed as four initialiser lists. Entry \( k \) of each list describes the \( k \) th split. So if you pass

{2,2,2}, {11,12,13}, {22,23,24}, {BottomUp,BottomUp,AggressiveTopDown}

then tree 2 will split three times. It will deploy 11, 12 and 13 cells to the ranks 22, 23 and 24, respectively. See the class documentation for when the splits happen.

Implementation

We do not work with initialiser lists internally. Instead, the constructor takes the list and transforms it into a sequence of Split objects which are then held internally.

I originally wanted the constructor to dumb some info on what type of load balancing exactly had been constructed. However, some codes use the load balancing object as static variable, so it might come up before the logging infrastructure is properly configured. This means that the logInfo will crash. So better stay silent and avoid logInfo() calls here.

Pitfall

At this point, I may not yet call validateAndAmend() or filter out the splits of other ranks, as this requires MPI to be properly initialised. If the hardcoded strategy is used as kind of a global variable - a typical application case - then the initialisation consequently breaks down. Both steps therefore happen in the first finishStep().

◆ Hardcoded() [2/2]

toolbox::loadbalancing::strategies::Hardcoded::Hardcoded ( std::initializer_list< int >  splittingTrees,
std::initializer_list< int >  numberOfCells,
std::initializer_list< int >  destinationRanks,
peano4::SplitInstruction::Mode  mode = peano4::SplitInstruction::Mode::BottomUp 
)

Shortcut around other constructor.

In this variant, all split modes are of the same type and there's a default. See the other constructor for details.

Member Function Documentation

◆ finishSimulation()

virtual void toolbox::loadbalancing::strategies::Hardcoded::finishSimulation ( )
overridevirtual

◆ finishStep()

virtual void toolbox::loadbalancing::strategies::Hardcoded::finishStep ( )
overridevirtual

Finish the current sweep.

Issue the split instructions

This routine hands out the splits that are due according to the rules in the class documentation. Upon the first call, it also drops all splits whose splitting tree is hosted by another rank, and it validates the remaining ones.

Disable strategy

If all the splits have been issued, the strategy disables itself. Usually, it is up to the user to disable a load balancing scheme if they want this to happen. For the hard-coded variant, this makes no sense: If there are no split instructions left over, we can as well just disable the strategy straightaway. This helps to speed up other things potentially, where a decision depends on the load balancing being deactivated already.

Update statistics

Standard step of any load balancing: update the global view of the statistics, and then also the blacklist. Eventually, we dump the statistics to the terminal.

Implements toolbox::loadbalancing::AbstractLoadBalancing.

◆ issueSplits()

void toolbox::loadbalancing::strategies::Hardcoded::issueSplits ( )
private

Hand out the splits that are due.

Runs through the splits in the order given and stops at the first split which is not due yet. See the class documentation for the rules.

◆ keepAndValidateLocalSplits()

void toolbox::loadbalancing::strategies::Hardcoded::keepAndValidateLocalSplits ( )
private

Drop the splits of other ranks and validate the remaining ones.

Is called by the first finishStep(). See the pitfall discussion in the constructor.

Field Documentation

◆ _hasKeptAndValidatedLocalSplits

bool toolbox::loadbalancing::strategies::Hardcoded::_hasKeptAndValidatedLocalSplits
private

Definition at line 334 of file Hardcoded.h.

◆ _log

tarch::logging::Log toolbox::loadbalancing::strategies::Hardcoded::_log
staticprivate

Definition at line 281 of file Hardcoded.h.

◆ _splits

std::list<Split> toolbox::loadbalancing::strategies::Hardcoded::_splits
private

Definition at line 332 of file Hardcoded.h.


The documentation for this class was generated from the following file: