Module Autotune

Empirical schedule search (autotuning)

tinygrad-style beam search over Ir.Schedule transforms, timed on the real device (docs/proposals/schedule-ir-optops.md; the search-harness half of the OptOps port). tune is a drop-in replacement for Context.compile: it compiles candidate schedules through the ?lowered_transform / ?lowered_transforms seams, times each on the context's device, and returns the routine of the fastest one. Every candidate (and the winner replay) derives from a hermetic copy of the one base lowering captured at the start — each candidate compile's own fresh lowering is ignored, because timing runs settle tensor-node value bounds and later lowerings can fold guards or re-segment fission differently, silently corrupting digest comparisons and replays. Winning schedules are persisted, in the structurally-rebindable saved form of Ir.Schedule_cache, to a disk cache keyed by the code's canonical digest and the backend, so a re-run of the same program skips the search (cross-process replay is guarded by digest equality against that process's own base lowering).

The candidate space:

Caveats (v1):

val extend_with_privatize : static_indices:Ir.Indexing.static_symbol list -> Ir.Schedule.schedule -> Ir.Low_level.optimized -> Ir.Schedule.schedule

The privatized preset extension used by the fissioned candidates: appends a Schedule.Privatize { target; over } for every materialized read-modify-write accumulator detected in the schedule's application to the segment — over being the outermost enclosing Serial loop whose symbol the access vector does not mention and whose subtree contains no hardware-typed loop. Each proposal is validated by try-applying the grown schedule against a hermetic copy of the segment (proposals violating the op's preconditions are dropped), so the result always applies cleanly where the input schedule does. Exposed for tests.

type report = {
  1. cache_hit : bool;
    (*

    The schedule came from the disk cache; no search ran.

    *)
  2. candidates_timed : int;
    (*

    Including the serial baseline.

    *)
  3. candidates_failed : int;
    (*

    Candidates rejected by op preconditions, hardware limits, or backend compilation.

    *)
  4. rounds_run : int;
    (*

    Beam-expansion rounds actually executed (0 = seeds only).

    *)
  5. sketch_candidates : int;
    (*

    Matmul-sketch instantiations seeded (0 when no matmul micro-kernel was detected or no tile sizes divide the extents). Deterministic given the computation and backend.

    *)
  6. fissioned : bool;
    (*

    The winning candidate compiles as multiple fissioned kernels.

    *)
  7. baseline_ms : float;
  8. best_ms : float;
  9. best_schedule : Ir.Schedule_cache.saved_schedule;
    (*

    The winner's schedule; for a fissioned winner, the concatenation of the per-segment schedules (informational).

    *)
}
val tune : ?beam_width:int -> ?rounds:int -> ?repeats:int -> ?seed_block_sizes:int list -> ?cache_dir:string -> ?timing_ctx:Context.t -> ?report:(report -> unit) -> Context.t -> Ir.Assignments.comp -> Ir.Indexing.unit_bindings -> Context.t * Context.routine

Like Context.compile, but returns the empirically fastest of the searched schedule candidates. The returned context/routine come from an ordinary sibling compile of ctx, so execution-dependency tracking behaves as if the winning compile were the only one. Raises like Context.run would (e.g. uninitialized inputs) — tune in the same state you would run in.