Wildmeshing Toolkit
Loading...
Searching...
No Matches
Classes | Public Types | Public Member Functions | Public Attributes | Private Member Functions | Private Attributes | List of all members
wmtk::ExecutePass< AppMesh > Struct Template Reference

Classes

struct  PassStats
 What the last pass cost in contention, as opposed to in work. More...
 

Public Types

using Tuple = typename AppMesh::Tuple
 

Public Member Functions

 ExecutePass (const ExecutionPolicy &policy_=ExecutionPolicy::kSeq)
 Construct a new Execute Pass object. It contains the name-to-operation map and the functions that define the rules for operations.
 
 ExecutePass (ExecutePass &)=delete
 
bool operator() (AppMesh &m, const std::vector< std::pair< Op, Tuple > > &operation_tuples)
 Executes the operations for an application when the lambda function is invoked. The rules that are customizly defined for applications are applied.
 
int get_cnt_success () const
 
int get_cnt_fail () const
 
const PassStatsstats () const
 

Public Attributes

std::map< Op, std::function< std::optional< std::vector< Tuple > >(AppMesh &, const Tuple &)> > edit_operation_maps
 A dictionary that registers names with operations.
 
std::function< double(const AppMesh &, Op op, const Tuple &)> priority
 Priority function (default to edge length)
 
std::function< bool(double)> should_renew = [](double) { return true; }
 check on wheather new operations should be added to the priority queue
 
std::function< std::vector< std::pair< Op, Tuple > >(const AppMesh &, Op, const std::vector< Tuple > &)> renew_neighbor_tuples
 renew neighboring Tuples after each operation depends on the operation
 
std::function< bool(AppMesh &, const Tuple &, int task_id)> lock_vertices
 lock the vertices concerned depends on the operation
 
std::function< bool(const AppMesh &)> stopping_criterion
 Stopping Criterion based on the whole mesh For efficiency, not every time is checked. In serial, this may go over all the elements. For parallel, this involves synchronization. So there is a checking frequency.
 
size_t stopping_criterion_checking_frequency = std::numeric_limits<size_t>::max()
 Cumulative successful operations before stopping_criterion is first consulted.
 
std::function< bool(const AppMesh &, const std::tuple< double, Op, Tuple > &t)> is_weight_up_to_date
 Should Process drops some Tuple from being processed. For example, if the energy is out-dated. This is in addition to calling tuple valid.
 
std::function< void(const AppMesh &, Op, const Tuple &t)> on_fail
 used to collect operations that are not finished and used for later re-execution
 
ExecutionPolicy policy
 
int num_threads = 1
 
size_t max_retry_limit = 10
 Attempts an operation gets at claiming its ring before it is handed to the serial queue drained after the barrier.
 
size_t deferral_window = 128
 Operations to run before handing deferred operations back to the queue, or 0 to wait until the queue empties.
 

Private Member Functions

void operation_cleanup (AppMesh &m)
 
size_t get_partition_id (const AppMesh &m, const Tuple &e)
 
void log_contention () const
 

Private Attributes

std::atomic_int cnt_success = 0
 
std::atomic_int cnt_fail = 0
 
PassStats m_stats
 

Constructor & Destructor Documentation

◆ ExecutePass()

template<class AppMesh >
wmtk::ExecutePass< AppMesh >::ExecutePass ( const ExecutionPolicy &  policy_ = ExecutionPolicy::kSeq)
inline

Construct a new Execute Pass object. It contains the name-to-operation map and the functions that define the rules for operations.

Note
the constructor is differentiated by the type of mesh, namingly wmtk::TetMesh or wmtk::TriMesh

Member Function Documentation

◆ log_contention()

template<class AppMesh >
void wmtk::ExecutePass< AppMesh >::log_contention ( ) const
inlineprivate

Debug-level because it is per pass and there are many passes per iteration. Enable with the logger at debug to see whether contention is worth acting on.

◆ operator()()

template<class AppMesh >
bool wmtk::ExecutePass< AppMesh >::operator() ( AppMesh &  m,
const std::vector< std::pair< Op, Tuple > > &  operation_tuples 
)
inline

Executes the operations for an application when the lambda function is invoked. The rules that are customizly defined for applications are applied.

Parameters
m
operation_tuplesa vector of pairs of operation's name and the Tuple to be operated on
Returns
true if finished successfully

Member Data Documentation

◆ deferral_window

template<class AppMesh >
size_t wmtk::ExecutePass< AppMesh >::deferral_window = 128

Operations to run before handing deferred operations back to the queue, or 0 to wait until the queue empties.

Bounding this is what makes the second-chance list pay. A per-thread queue holds thousands of operations, so draining only at queue-empty means a deferred operation waits that long, by which time the mesh around it has moved, is_weight_up_to_date rejects it, and the work has to be rediscovered. That inflates the attempt count by roughly a fifth and eats most of the per-operation saving.

Measured at 16 threads, three reps, optimization wall time against the pre-deferral baseline, with attempts in brackets:

               103197 (base 23.0M)      101881 (base 48.8M)
 window 0       -2.2%  [28.5M]          -20.4%  [45.7M]
 window 32     -16.3%  [23.7M]          -21.6%  [43.7M]
 window 128    -17.9%  [23.1M]          -26.4%  [41.9M]
 window 512    -17.0%  [23.7M]          -25.5%  [42.7M]

128 is at or near best on both and keeps the attempt count closest to the baseline's. The result is not sharp between 32 and 512; it is sharp against 0.

◆ is_weight_up_to_date

template<class AppMesh >
std::function<bool(const AppMesh&, const std::tuple<double, Op, Tuple>& t)> wmtk::ExecutePass< AppMesh >::is_weight_up_to_date
Initial value:
= [](const AppMesh& m, const std::tuple<double, Op, Tuple>& t) {
assert(std::get<2>(t).is_valid(m));
return true;
}

Should Process drops some Tuple from being processed. For example, if the energy is out-dated. This is in addition to calling tuple valid.

◆ lock_vertices

template<class AppMesh >
std::function<bool(AppMesh&, const Tuple&, int task_id)> wmtk::ExecutePass< AppMesh >::lock_vertices
Initial value:
=
[](const AppMesh&, const Tuple&, int task_id) { return true; }

lock the vertices concerned depends on the operation

◆ max_retry_limit

template<class AppMesh >
size_t wmtk::ExecutePass< AppMesh >::max_retry_limit = 10

Attempts an operation gets at claiming its ring before it is handed to the serial queue drained after the barrier.

10 was swept and left alone. Measured on 128k-tet Thingi10K 103197 at 16 threads, three reps each, in µs per attempted operation:

immediate requeue (before the second-chance deferral below existed): 1: +17.0% 2: +7.2% 3: +6.2% 5: +4.6% 10: best 20: +6.2% with the deferral: 2: 2.170 3: 1.969 10: 1.974 (baseline at 10 was 2.527)

So under the old immediate-requeue behaviour the value mattered a lot – a low limit sent everything it stopped retrying to the serial queue, and that tail (23% of scheduler time at 10, 39% at 1) cost more than the spinning it avoided. With the deferral the retries are nearly free, the overflow pressure disappears, and 3 and 10 become indistinguishable. Left at 10 because nothing argues for moving it.

Below about 3 it still hurts: at 2 the overflow rate climbs enough to be visible again.

◆ on_fail

template<class AppMesh >
std::function<void(const AppMesh&, Op, const Tuple& t)> wmtk::ExecutePass< AppMesh >::on_fail
Initial value:
=
[](const AppMesh&, Op, const Tuple& t) {}

used to collect operations that are not finished and used for later re-execution

◆ priority

template<class AppMesh >
std::function<double(const AppMesh&, Op op, const Tuple&)> wmtk::ExecutePass< AppMesh >::priority
Initial value:
=
[](const AppMesh&, Op, const Tuple&) { return 0.; }

Priority function (default to edge length)

◆ renew_neighbor_tuples

template<class AppMesh >
std::function<std::vector<std::pair<Op, Tuple> >(const AppMesh&, Op, const std::vector<Tuple>&)> wmtk::ExecutePass< AppMesh >::renew_neighbor_tuples
Initial value:
=
[](const AppMesh&, Op, const std::vector<Tuple>&) -> std::vector<std::pair<Op, Tuple>> {
return {};
}

renew neighboring Tuples after each operation depends on the operation

◆ stopping_criterion

template<class AppMesh >
std::function<bool(const AppMesh&)> wmtk::ExecutePass< AppMesh >::stopping_criterion
Initial value:
= [](const AppMesh&) {
return false;
}

Stopping Criterion based on the whole mesh For efficiency, not every time is checked. In serial, this may go over all the elements. For parallel, this involves synchronization. So there is a checking frequency.

◆ stopping_criterion_checking_frequency

template<class AppMesh >
size_t wmtk::ExecutePass< AppMesh >::stopping_criterion_checking_frequency = std::numeric_limits<size_t>::max()

Cumulative successful operations before stopping_criterion is first consulted.

Despite the name this is a threshold, not a period: the count it is tested against is never reset, so once the pass has had this many successes the criterion is consulted after every subsequent operation. Both current users rely on exactly that – they set the criterion to return true and the threshold to the number of collapses needed to reach a target vertex count, making this a decimation counter that stops the pass on its first check. It is not a "check every N operations" knob, and writing a genuinely periodic criterion against it would evaluate that criterion on every operation forever after.

Left at the default, the criterion is never consulted at all, which is the case for every tetwild/triwild/simwild pass.

(There used to be a cnt_update member here that was incremented per success and reset inside the check, as if the threshold were a period. Nothing ever read it – the reset was on a branch the always-true criteria above never reach – so it was one more contended atomic on the hot path buying nothing, and it is gone.)


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