Files
Raptor/docs/compiler/scheduling-implementation.md
T
NiccoloN 034c197260
Validate Operations / validate-operations (push) Failing after 3h10m55s
rework documentation
add plot generation scripts
adjust validation tools
add up-to-date results
2026-10-19 16:34:01 +02:00

6.0 KiB

Scheduling implementation notes

The scheduling overview maps ownership and summarizes costs/complexity. Normative graph, pipeline, synchronization, and target contracts are linked there; the details below describe current implementation choices.

Publication, order, and local materialization

materializeScheduledCompute records graph IDs for publication identity, not execution order. Class lanes retain graph encounter order independently of physical core IDs, preserving regular source-lane mappings. Within each core, instances sort by verified scheduler slots. Only consecutive compatible instances form homogeneous runs; class-lane ordering must not reorder a core's work.

insertLocalFragment replaces a complete singleton insertion with a leading unit-dimension reshape view only when:

  • source and destination have static shapes;
  • destination rank is source rank + 1, with leading dimension 1 and remaining dimensions exactly equal to the source shape;
  • element type and encoding match;
  • offset/size/stride lists all match destination rank; and
  • every offset is statically zero, every size is the full corresponding destination dimension, and every stride is statically one.

This includes scalar sources. Other insertions retain tensor.insert_slice semantics rather than being converted into views.

Receive forwarding belongs to PimBufferizationPass.cpp, after copy lowering and contiguity normalization. It requires a private allocation and single-use receive/copy chain in the same block, zero source and receive offsets, and equal sizes. The receive can sink to the copy target past memory-effect-free operations and memref.load (including lane lookup loads). Writes, synchronization, and other effectful operations are barriers. Forwarding does not establish synchronization policy.

Transfer accounting and transpose costs

ComputeGraph.cpp uses the same cached publication map for dependency extraction and projected transfer volume. Physical slots map back to graph lanes; graph-range overlap is not a byte estimate. Blueprint edges belong to this shared graph for both scheduling strategies.

Exact projections charge selected physical fragments individually, including each fragment's byte, endpoint-memory, and packet rounding. Conservative batch dependencies charge a producer chunk as one aggregate: scale element count by chunk lanes, multiply by effective bitwidth, then round to bytes once before memory-access/flit rounding. Dividing an already rounded whole-result byte count would lose sub-byte payloads. These two granularities are deliberate. Spatial can analyze sub-byte storage; PIM communication lowering requires byte-sized elements. Blueprint assembly charges per fragment. A full fragment receive directly into assembly storage omits duplicate endpoint-memory edge cost; later copy elimination can leave this estimate conservative.

Execution transpose costs share getTransposeRun with lowering: round memory reads/writes per run and add vector execution when source stride is not one. The Pimsim vector unit charges vector payload bytes, not the full strided address footprint. The compiler model follows that accounting; instruction setup/issue and contention remain approximations, not extra inferred memory traffic.

Priorities, placement, and recovery limits

PipelineScheduling.cpp caches a per-template adjustment between physical execution cost and nominal volume priority, scaling it by chunk lanes. Partitioning and packing use the nominal priorities; transposes count as bulk moves there. PEFT, final schedule evaluation, and reported per-core demand use physical estimates. These quantities must not be substituted for one another when interpreting results.

Placement compares maximum per-core serial compute plus incident same-stage transfer demand, then completion/span and compute load. Each participating core is charged incident transfer demand as a placement proxy; those charges are not summed twice into elapsed time. Within 1% demand difference, span breaks the tie. Ordered packing plus dependency-tail priority biases cuts away from long dependent groups; it is not a throughput prediction. Measured simulator periods remain authoritative, including when a proxy improves.

Core budgets come from stage allocation and bounded capacity recovery. Failed packing budgets are not lower bounds. PEFT reservations prefer weight reuse when distinct weights exceed half the fleet's one-copy resident capacity; otherwise they favor communication/load. The reuse-first constructor supplies feasibility recovery without replacing a successful reservation, and can concentrate work. No exact combinatorial solver proves infeasibility after heuristic failure.

ResidentWeightPacking.hpp adds at most 17 constructions after initial failure: one reverse traversal, then up to 16 single-choice replays sharing 65,536 candidate checks. Scratch buffers are reused; an extra O(T) failed-assignment snapshot is created only after both initial orders fail. Weight indexing still has O(R W) worst-case equality work; partition DP and latency insertion-gap searches remain superlinear as listed in the scheduling overview. Cached PEFT center distances avoid a topology scan per task/candidate; projection and priority analyses share/cache work rather than revisiting every template body per chunk. Ready-transfer signature searches remain another potential compile-time hotspot.