gqa(adr): ADR-0064/0065 second-pass review — 1024B rationale, FIFO as ordering source, robust formula-based tests, D5/D6 auto-bind alignment
ADR-0064 Revision 2 second-pass review fixes: - D7 added 1024-byte cap rationale: explicitly framed as a *safe engineering limit* (intentionally above all known composites at ~322 bytes, finite descriptor capacity placeholder), not a measured HW number. Default is the discipline; topology override for real HW. - D7 segment ordering: strict FIFO is the *ordering source*; rw_handles is dependency metadata, not the ordering primitive. Explicit note for the future RW-aware reorder migration path. - Tests rewritten around the formula (D1) instead of specific numbers: - Test #1 → "formula preservation" (parametrized across composites, asserts dispatch == FIXED + bytes × R, not anchor 43 ns). - Test #2 → robust "opt3 > 2 × opt2" qualitative gate; ≈ 4.0× is informative only, not the gate, so calibration changes don't break. - Test #9 → qualitative (opt2 < opt3 at all R), not a numeric ratio. - Anchor description in D3 stays as *informative* (shows the model produces ≈ 43 ns at defaults), but is no longer a test gate. ADR-0065 second-pass review: - D5 step 5 aligned with D6.6 invariant: explicit conflict check before auto-binding (previously written as if auto-bind always happens). Mirrors D6.6 wording: error if both kernel explicit `a` and prologue primary_out are present. - Test #7 made robust: `opt3 > 2 × opt2` gate (was ≈ 4.0×). Default-calibration model expectation (≈ 4.0×) recorded as informative reference, not the gate. DDD-0065 follow-on robust thresholds: - §1.4 success criteria: ratio condition rewritten as `opt3 > 2 × opt2` with model-expected ≈ 4.0× as informative. - §9 P6 phase gate: `opt3 > 2 × opt2` (was 4.0× ± 15%). - §10 dispatch ratio test: assert `opt3 > 2 × opt2`; record observed ratio for performance tracking but do not gate on it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -251,15 +251,34 @@ per D4). Oversized commands are deterministically **segmented** by the
|
||||
- The segmentation algorithm is deterministic (greedy by op index in
|
||||
emit order); it is part of the host emitter, not PE_SCHEDULER.
|
||||
|
||||
Why this is needed. Real hardware imposes hard limits — descriptor
|
||||
**Why a cap is needed.** Real hardware imposes hard limits — descriptor
|
||||
queue entry size, scheduler parser buffer, command SRAM, firmware
|
||||
input. Without a cap, the `FIXED + bytes × R` model would reward
|
||||
arbitrarily large fused composites beyond what hardware accepts (e.g.,
|
||||
fusing 100 primitive ops into one composite, paying one `FIXED`).
|
||||
A composite of (say) 100 ops at ~30 bytes each = ~3000 bytes >
|
||||
1024 cap → segmented into 3 ≈ 3 × FIXED, restoring honest accounting.
|
||||
|
||||
Decode opt2's `#2` composite (10 ops, ~310 bytes) sits comfortably
|
||||
**Why 1024 bytes specifically.** This is a **safe engineering limit**,
|
||||
not a measured HW number — intentionally chosen to be well above all
|
||||
currently known composites (decode opt2's `#2` at ~322 bytes is the
|
||||
largest in the kernbench codebase) while still representing a *finite*
|
||||
descriptor capacity that future recipes must respect. The number is
|
||||
overridable per topology (D4); when a real HW reference appears, the
|
||||
value should be recalibrated. The role of this default is to make the
|
||||
cap *exist as a discipline*, not to fit a specific HW.
|
||||
|
||||
**Ordering of segments — driven by strict FIFO, not `rw_handles`.**
|
||||
Segments are emitted into the PE_CPU → PE_SCHEDULER queue in their
|
||||
emit order. Strict-FIFO dispatch (ADR-0065 D6.3) is the *ordering
|
||||
source*: segments execute in the order they enter the queue. The
|
||||
`rw_handles` block on each segment is **dependency metadata** for the
|
||||
cross-composite hazard tracker — it does **not** by itself guarantee
|
||||
inter-segment ordering. If a future scheduler relaxed FIFO (e.g., to
|
||||
RW-aware reorder, ADR-0065 A4), the segmenter would need to either
|
||||
(a) merge segments into a single CompositeCmd, or (b) introduce an
|
||||
explicit completion-handle dependency chain. For Phase 1 this does
|
||||
not arise: strict FIFO is in effect.
|
||||
|
||||
Decode opt2's `#2` composite (10 ops, ~322 bytes) sits comfortably
|
||||
inside the 1024 cap — no segmentation for the GQA workload.
|
||||
|
||||
## Alternatives
|
||||
@@ -331,13 +350,27 @@ calibration shows it matters.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
1. **Anchor preservation.** A single-OpSpec composite for a DMA-staged
|
||||
GEMM path (`logical_bytes ≈ 54`) dispatches in ≈43 ns at default
|
||||
values (within ±2 ns).
|
||||
2. **Structural ratio.** opt3 vs opt2 dispatch (per ADR-0065 §verification):
|
||||
`opt3 / opt2 ≈ 4.0×` at default calibration.
|
||||
Tests are written against the **formula** (D1), not against specific
|
||||
numeric anchors, so they remain valid when calibration changes or when
|
||||
OpSpec/CompositeCmd fields are added.
|
||||
|
||||
1. **Formula preservation.** For any `CompositeCmd` `c`, PE_CPU's
|
||||
recorded dispatch overhead equals `FIXED_PER_CMD + c.logical_bytes
|
||||
× R` (within ±1 cycle for floor/round-off). Parametrized over
|
||||
several composites: a 1-OpSpec GEMM composite, a 5-op MATH chain,
|
||||
and a 10-op recipe composite. Default-calibration numbers (anchor
|
||||
≈43 ns for the 1-OpSpec composite) are informative reference, not
|
||||
the test gate — the test gate is the formula equality.
|
||||
2. **Qualitative ratio (robust).** opt3 per-tile PE_CPU dispatch
|
||||
strictly exceeds opt2 per-tile dispatch by at least a 2× margin —
|
||||
`opt3 > 2 × opt2`. The default-calibration model predicts ≈4×;
|
||||
the gate is the loose 2× bound so the test does not break when
|
||||
calibration is moved (e.g., when a HW reference replaces the
|
||||
default). Informative numbers — see ADR-0065 §verification and
|
||||
DDD-0065 §11 for the model expectation.
|
||||
3. **Override path.** Topology yaml `pe_cost_model:` block changes the
|
||||
per-PE dispatch cost; default is recovered when block is missing.
|
||||
The formula identity from #1 must hold with the override values.
|
||||
4. **`PeCpuOverheadCmd` bypass.** Manual `tl.cycles(n)` issues exactly `n`
|
||||
cycles, not `n + dispatch_cycles(...)`.
|
||||
5. **No double-count.** PE_DMA `drain_ns`, PE_GEMM/MATH `_compute_ns`
|
||||
@@ -348,12 +381,14 @@ calibration shows it matters.
|
||||
dispatch-cycle accounting for the same kernel.
|
||||
8. **Composite size cap (D7).** A recipe that would emit `logical_bytes
|
||||
> MAX_COMPOSITE_LOGICAL_BYTES` is segmented into N consecutive
|
||||
`CompositeCmd`s; total dispatch = sum of segment dispatches; RW
|
||||
ordering across segments preserved by strict FIFO.
|
||||
9. **Sensitivity sweep.** At `R ∈ {0.25, 0.0625, 0.03125}` cycles/byte
|
||||
(= 4 / 16 / 32 bytes/cycle), the conclusion *opt2 per-tile dispatch
|
||||
< opt3 per-tile dispatch* must hold (ratio monotonically increases
|
||||
as R decreases — FIXED dominates more).
|
||||
`CompositeCmd`s; `sum(segment.logical_bytes) == original
|
||||
logical_bytes`; total dispatch = sum of segment dispatches (FIXED
|
||||
paid per segment); inter-segment ordering preserved by strict FIFO
|
||||
(not by `rw_handles` alone).
|
||||
9. **Sensitivity (qualitative).** At `R ∈ {0.25, 0.0625, 0.03125}`
|
||||
cycles/byte, `opt3 > opt2` at *all* three points. Direction (ratio
|
||||
monotonically increases as R decreases) is also asserted, but
|
||||
absolute ratio values are *not* required.
|
||||
|
||||
## Migration
|
||||
|
||||
|
||||
Reference in New Issue
Block a user