260 lines
17 KiB
Markdown
260 lines
17 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.
|
|
|
|
`CopyAccessPlan` shares ordered contiguous-run geometry between projection
|
|
costing and PIM copy normalization. Its inputs are a logical copy shape and
|
|
proven source and destination strides, in elements. The immutable result keeps
|
|
outer dimensions, run count, run length, and relative offsets compact. Building
|
|
the plan takes linear time and space in rank, independent of loop trip counts.
|
|
It creates no IR and does not prove aliasing, storage ownership, or copy legality.
|
|
PIM bufferization still owns those decisions and the mapping to byte accesses.
|
|
The plan alone does not describe a complete scheduled memory-request stream.
|
|
Construction rejects an unrepresentable maximum relative element offset,
|
|
including the last element of each run. Callers still check byte scaling and
|
|
base-address addition.
|
|
Projection costing uses checked row-major strides. Unsupported flattened
|
|
offsets or strides produce at most one warning per graph cost query. The query
|
|
uses one separately rounded request per element as a conservative service bound.
|
|
It does not reject otherwise valid Spatial IR. Target lowering retains its own
|
|
address and layout legality checks. Unsupported estimates and malformed IR are
|
|
different conditions.
|
|
|
|
The PIM adapter's `planComputeInputMaterialization` reads scheduled Spatial
|
|
regions without changing them. It returns `Unused`, `ForwardConstant`,
|
|
`ForwardFunctionInput`, or `CopyAtEntry`. Function-input normalization forwards
|
|
host inputs into compute bodies before entry-copy materialization.
|
|
Single-core lowering forwards used arithmetic constants. Batch lowering copies
|
|
remaining inputs at body entry, including constants. Lowering and costing
|
|
consume the same decisions. The query has constant time and space per
|
|
input and does not expand lanes or loops. Abstract target work extraction uses
|
|
the same query. Subsequent canonicalization can remove uses.
|
|
`getReusableOutputTensor` shares read-only destination selection with vector
|
|
lowering. It identifies a tensor operand or requires fresh storage. It does not
|
|
prove a physical alias, assign an address, or replace one-shot bufferization.
|
|
The result is valid only for the current SSA graph. Changes to uses, operation
|
|
order, loop yields, or operands require recomputation. Lowering consumes this
|
|
decision immediately; it does not cache values across rewrites. Current PIM
|
|
bufferization consumes the explicit destination ties introduced by target
|
|
lowering. Output-copy forwarding also consumes the resulting allocation,
|
|
subview, copy, and dominance facts. An early destination choice alone cannot
|
|
describe those physical aliases or the final storage lifetimes.
|
|
Logical storage identity and physical address equality are different facts.
|
|
Local-memory planning can assign the same address to distinct allocations whose
|
|
lifetimes do not overlap. Scalar register setup therefore needs placement and
|
|
instruction-order information in addition to logical alias relationships.
|
|
Abstract cost estimates need not reconstruct these addresses.
|
|
|
|
This input plan does not count physical constant loads. A forwarded host
|
|
constant can need a new local buffer at each use. Bufferization, host folding,
|
|
alias analysis, and memory normalization determine the resulting requests.
|
|
`isZeroBitPatternSplat` shares constant-content classification between graph
|
|
accounting and code generation. PIM replaces eligible positive-zero and integer-zero
|
|
loads with local fills. Negative floating zero retains its distinct bit pattern.
|
|
Constant content is candidate-independent; input placement and buffer reuse
|
|
depend on the realized candidate. Production uses these shared decisions when
|
|
extracting Spatial execution events for the queue estimate.
|
|
|
|
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.
|
|
|
|
### Spatial execution costs
|
|
|
|
`SchedulingTarget::estimateRealizedScheduleCost` is a read-only query on realized Spatial IR.
|
|
The PIM adapter extracts compact ordered execution events without constructing PIM operations.
|
|
It uses physical core assignments, communication, input-materialization decisions, and shared copy geometry.
|
|
No physical buffer addresses are required.
|
|
The shared engine models controller arbitration, source FIFOs, synchronization, and repeated generations.
|
|
Both arbitration policies are sensitivity checks, not guaranteed bounds.
|
|
The engine returns typed intervals and recurrence evidence before diagnostic serialization.
|
|
The target adapter adds static lowering support. Spatial owns the selection policy margin.
|
|
JSON fields are report output and do not control availability or eligibility.
|
|
Typed evaluation completes before reporting. Serialization never runs queue analysis.
|
|
Only optional diagnostics construct fingerprints, traces, and per-core resource reports.
|
|
|
|
Normal compilation extracts costs for multiple candidates and lowers only the selected schedule.
|
|
A single candidate bypasses the cost query entirely.
|
|
Candidate summaries distinguish available timing, static lowering support, and proved modeled recurrence.
|
|
Unknown support remains explicit. All candidates must satisfy these checks before comparison.
|
|
`Unsupported` identifies a known static violation, including invalid or repeated physical-core assignments.
|
|
`Unknown` identifies missing evidence or an unmodeled form, rather than a proven backend rejection.
|
|
`Supported` covers checked static forms and does not prove final allocation success.
|
|
A finite interval alone does not establish eligibility.
|
|
|
|
Preflight does not repeat bufferization or allocation.
|
|
The backend owns final legality and signed 32-bit local-memory address validation.
|
|
Resource failures marked by that owner permit an explicit retry from the original checkpoint.
|
|
Unclassified failures stop compilation. Every attempted schedule and cause appears in the selection report.
|
|
|
|
### Complete-state recurrence
|
|
|
|
Queue analysis checks translated states at one fixed core's body entry.
|
|
Snapshots contain completion counts relative to core entry, ready-input counts, and busy state for every event.
|
|
Run-length encoding keeps these records compact.
|
|
Snapshots also contain pending events in arbitration order, remaining FIFO tokens, controller availability, and the tie policy.
|
|
One pending firing per event makes node IDs sufficient to resolve simultaneous-event ties.
|
|
The graph and target parameters remain fixed within each query.
|
|
Time offsets use checked unsigned differences.
|
|
Equal states require equal positive generation progress on every core.
|
|
This proves recurrence for the deterministic abstract model, not physical execution-time accuracy.
|
|
Reporting counters and analysis budgets are not hardware state.
|
|
|
|
Token initialization distances and graph edges remain immutable within a query.
|
|
Consumption clears a drained queue and resets its head immediately.
|
|
Drained and never-populated queues therefore have the same readiness state.
|
|
Consumed prefixes, capacities, and compaction indices cannot affect future timing or FIFO contents.
|
|
An idle controller's past completion cannot delay future arrivals.
|
|
These normalizations require causal, monotonic transitions, which the proof checks.
|
|
|
|
Proof construction uses at most 256 generations and 16 MiB of snapshot storage.
|
|
Each checkpoint scans events, dependencies, and remaining tokens, then orders a copy of the pending frontier.
|
|
Exact comparisons use bounded history; hashes only filter comparisons.
|
|
Unproved recurrence remains explicit and cannot guide multi-candidate selection.
|
|
When recurrence is proved, its interval is authoritative for that arbitration policy and its report.
|
|
Proof-mode reports retain interval-window results under `legacy_interval_window`.
|
|
Normal costing stops once recurrence is proved. Detailed diagnostics can continue to collect interval-window evidence.
|
|
A legacy event or generation limit cannot override a later complete-state proof.
|
|
Budget limits, lack of ready events, and unproved recurrence remain separate evidence.
|
|
Internal diagnostic queries can cap interval-window work without reducing the recurrence proof budget.
|
|
The standalone instruction diagnostic keeps its interval-window behavior when recurrence is not requested.
|
|
|
|
### Approximation and diagnostics
|
|
|
|
Scalar setup, copy forwarding, local reuse, and host-slice folding can change request readiness.
|
|
These effects remain prediction limitations. Physical address reuse itself adds no execution work.
|
|
Direct returns use the same return-operand test as lowering and preserve output multiplicity.
|
|
Captured weights used by vector operations require host loads.
|
|
Publication-only slices of compact results use `CopyAccessPlan` runs without an intermediate copy.
|
|
Matrix writeback requires complete aligned array groups.
|
|
Partial output groups, encoded layouts, unavailable geometry, and indirect output collection produce unavailable costs.
|
|
|
|
`--pim-periodic-diagnostics` adds resource summaries and ordered-request fingerprints.
|
|
Normal compilation creates no fingerprints, detailed traces, or sensitivity runs.
|
|
Optional local-copy sensitivity removes modeled copy work without changing generated code.
|
|
It does not describe a legal transformed program or an interval bound.
|
|
The standalone instruction diagnostic remains available for offline timing validation.
|
|
|
|
Checked arithmetic rejects unrepresentable sizes, loop bounds, repeated work, and service durations.
|
|
Work-only repeats remain compact. Immutable summaries are cached within a query.
|
|
Ordered memory and communication occurrences expand within the event budget.
|
|
For `P` compact records, `R` expanded events, and `D` dependencies, construction costs `O(P+R+D)` time.
|
|
With `B` processed events and `T` token propagations, queue evaluation costs `O(B log R+T)` time.
|
|
Storage costs `O(P+R+G D)` for bounded generation count `G`, plus bounded recurrence history.
|
|
Reports separate extraction, expansion, and queue-analysis times.
|
|
Fingerprints compare memory and blocking-operation order, without addresses or work between requests.
|
|
Equal fingerprints therefore do not prove equal request readiness or timing.
|