Class CostModel

Class Documentation

class CostModel

Bundles the optimizer’s own cost closures (memsize/flops/roofline) behind one value type so dry-run Results report model size (not an allocated size), and the harness can additionally read FLOPs and projected execution cost per operation.

This is a thin wrapper: all arithmetic is delegated verbatim to sequant::opt::detail::memsize_counter / flops_counter / roofline_op_cost (see core/optimize/single_term_detail.hpp and core/optimize/cost_model.hpp) — no parallel cost model is implemented here. The only thing this class adds is the ExtentOverrides indirection: each query builds a fresh (cheap; no heap allocation beyond the closure itself) index-to-extent callable that consults overrides before falling back to the SizeRegime’s nominal extent, then hands that callable to the counter.

Public Functions

inline explicit CostModel(SizeRegime regime, RooflineParams roofline = {})
inline std::size_t memsize(container::svector<Index> const &idxset, ExtentOverrides const &overrides = {}) const

Bytes for a tensor with these (literal, canon-order) indices, honoring any per-index extent override (a runtime slice_mode()/ mode_batches() narrowing).

Delegates the extent-product / composite-moment math to memsize_counter, invoked with idxset as the sole (lhs) operand and empty rhs/result &#8212; an empty operand’s tot_indices() split accumulates the starting product of 1.0, which memsize_counter itself special-cases to contribute zero bytes, so this reproduces exactly the single-operand byte count memsize_counter is designed to report per operand.

inline double flops(container::svector<Index> const &out, container::svector<Index> const &contracted, container::map<Index, std::size_t> const &label_extents = {}) const

Multiply-add count for a contraction whose free (result) indices are out and whose contracted (summed-over) indices are contracted.

Delegates to flops_counter, which prices the union of its (lhs, rhs, result) arguments; passing (out, contracted, {}) makes that union exactly out U contracted &#8212; the full index set touched by the contraction, since by construction contracted holds precisely the indices present in both operands but absent from the result.

label_extents maps an annotation label (an Index appearing in out or contracted) to its runtime-realized (sliced) extent. Unlike the value’s positional ExtentOverrides, this is keyed by Index because out / contracted are labels &#8212; the op’s annotation is the sole source of labels. DryRunOps::prod builds it from each operand’s positional overrides via that operand’s annotation (see extents_by_label).

inline double exec_cost(double flops_count, std::size_t left_bytes, std::size_t right_bytes, std::size_t result_bytes) const

Roofline-projected execution cost of one contraction (see sequant::opt::detail::roofline_op_cost).

left_bytes / right_bytes / result_bytes are the both-operands- and-result footprints in bytes (as reported by Result::size_in_bytes()); converted to elements (the counter’s native unit) via numeric_size before delegating. All three are charged, because roofline_op_cost's traffic is the compulsory single-pass data movement of one contraction: read both operands, write the result. This is exactly what the optimizer’s DP charges (S[lp] + S[rp] + S[n]; see PeakModel::relax / BatchedPeakModel::relax in core/optimize/cost_model.hpp), so a dry-run replay of a DP-chosen tree prices each op the same way the DP did, at realized (sliced) extents. Charging fewer footprints (e.g. only the left operand) both under-counts the traffic and makes the cost depend on operand order, which the contraction’s data movement does not. The finite-cache re-read effect is not this term &#8212; it is the separate Hong-Kung bound inside roofline_op_cost.

inline SizeRegime const &regime() const noexcept
inline void set_cost_sink(CostSink *sink) const noexcept

Attach (or detach with nullptr) the optional replay cost sink.

Const because the CostModel is shared as shared_ptr<CostModel const> by every dry-run Result token; a metered replay sets this on its one shared model just before the Trace::On replay so each product op can fold into it. The pointee (a CostSink) is external and owns the mutable state; this only records where to fold. Off by default => no fold, and the dry-run backend meters nothing.

inline void tally_op(double flops_count, double exec) const noexcept

Fold one product op’s sliced-extent flops_count / exec into the attached sink (no-op when none is attached).

Called at each actual product execution in the replay, so a contraction re-executed once per occ block is tallied once per block at its sliced size &#8212; exactly the recompute signal (see CostSink).