Module Ir.Cost_model

Analytic cost model, the extraction half (gh-ocannl-491 task 1): per-kernel compulsory memory footprints, arithmetic-op counts, arithmetic intensity, and the roofline lower-bound time under advisory envelope constants (Backend_intf.hardware_limits's peak_flops / peak_memory_bandwidth).

The analysis is pure and backend-free: it reads a (typically optimized) Low_level.t through the affine-access artifacts of gh-ocannl-494 (Low_level.affine_accesses, Affine.fiber_cardinality) and returns numbers; envelope constants are plain floats supplied by the caller. The model ranks candidate schedules — it does not predict runtimes.

Approximation contract (each direction is explicit, all biases point the same way):

type node_footprint = {
  1. fp_read_bytes : int;
    (*

    Distinct cells read (including accumulation reads) times byte width.

    *)
  2. fp_write_bytes : int;
    (*

    Distinct cells written times byte width.

    *)
  3. fp_rmw_bytes : int;
    (*

    The subset of fp_write_bytes written by read-modify-write accumulations (a_rmw).

    *)
  4. fp_approx : bool;
    (*

    true when any contributing count is an upper bound rather than exact (see the module contract for the causes).

    *)
}
val sexp_of_node_footprint : node_footprint -> Sexplib0.Sexp.t
type summary = {
  1. per_node : (Tnode.t * node_footprint) list;
    (*

    In first-access program order.

    *)
  2. read_bytes : int;
    (*

    Sum of fp_read_bytes over per_node.

    *)
  3. write_bytes : int;
    (*

    Sum of fp_write_bytes over per_node.

    *)
  4. flops : int;
    (*

    Whole-kernel arithmetic-op count (loop extents times per-statement ops).

    *)
  5. flops_approx : bool;
    (*

    true when guarded (If) code contributed (guards-taken bound).

    *)
  6. opaque : bool;
    (*

    true when the code contains Staged_compilation or merge-buffer reads: some traffic and ops are invisible to the analysis, so counts may UNDER-estimate.

    *)
}
val sexp_of_summary : summary -> Sexplib0.Sexp.t
val analyze : Low_level.t -> summary

The whole-kernel extraction: footprints from Low_level.affine_accesses image cardinalities, op counts from a loop-nest walk. Input is typically post-optimize code — the analysis charges whatever materialization the code actually performs, so virtual/inlined nodes never appear and recomputation is charged as ops, not bytes.

val total_bytes : summary -> int

read_bytes + write_bytes: the "bytes moved" denominator of arithmetic intensity — an accumulation's cells are charged once in each direction.

val arithmetic_intensity : summary -> float

FLOPs per byte moved, flops / max 1 (total_bytes).

val roofline_seconds : ?peak_flops:float -> ?peak_memory_bandwidth:float -> flops:int -> bytes:int -> unit -> float option

The roofline lower-bound time: max (flops/peak_flops) (bytes/peak_memory_bandwidth) over the envelope constants present (peak_flops in FLOP/s, peak_memory_bandwidth in bytes/s — the advisory Backend_intf.hardware_limits fields); None when neither is given. Monotone: raising either constant never increases the bound. A lower bound only up to the model's upper-bound byte/op counts — rank with it, do not predict.