Files
Raptor/docs/compiler/scheduling-implementation.md
T
NiccoloN 48e9fb3795
Validate Operations / validate-operations (push) Failing after 3h13m5s
better throughput scheduling
update results_comparison.csv
2026-10-19 22:11:51 +02:00

121 lines
7.3 KiB
Markdown

# Scheduling implementation notes
The [scheduling overview](scheduling.md) 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
Deferred transfer planning allocates one host slot for repeated full-fragment
requirements with the same physical producer, payload type, and source slot.
One immutable producer result identifies one logical inference generation.
Scalar source projections retain separate publications until their exact
geometry has a common representation in both analyses.
Boundary planning moves a shared publication's notifications to its earliest
existing send boundary. The first notification stores the payload before READY;
later notifications emit READY only. Each reader retains its load and READY
count. FREE planning includes every physical reader before the first overwrite
of the shared slot. Existing unique writer-reader releases, register allocation,
capacity-driven FREE-group merging, and restart barriers remain in force.
Host-slot allocation and shared-boundary planning take expected linear time
and space in the number of physical consumer transfers. Structured send runs
retain compact per-lane store flags. They do not duplicate payload shaping
for notification-only readers.
[`materializeScheduledCompute`](../../src/PIM/Dialect/Spatial/Passes/Transforms/MergeComputeNodes/ScheduledComputeMaterialization.cpp) 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`](../../src/PIM/Dialect/Pim/Passes/Transforms/Bufferization/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`](../../src/PIM/Dialect/Spatial/Passes/Transforms/MergeComputeNodes/Scheduling/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](../../backend-simulators/pim/pimsim-nn/src/core/component/VectorUnit.cpp)
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`](../../src/PIM/Dialect/Spatial/Passes/Transforms/MergeComputeNodes/Scheduling/PipelineScheduling.cpp) caches a per-template
adjustment between physical execution cost and nominal volume priority, scaling
it by chunk lanes. The original partitioning and packing candidate uses nominal
priorities; transposes count as bulk moves there. The memory-aware candidate
uses execution estimates for partitioning, retaining the existing core-budget
and packing heuristics. Final
selection evaluates bounded alternatives after physical placement, retaining
the original candidate when the combined resource estimates worsen. PEFT,
final schedule evaluation, and reported per-core demand use physical estimates.
These quantities must not be substituted for measured simulator periods.
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](../../src/PIM/Dialect/Spatial/Passes/Transforms/MergeComputeNodes/Scheduling/PeftScheduler.cpp)
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`](../../src/PIM/Dialect/Spatial/Passes/Transforms/MergeComputeNodes/Scheduling/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.