gqa(adr): ADR-0064 Rev2 (structural dispatch cost) + ADR-0065/DDD-0065 (flat-ops composite + softmax_merge recipe)
ADR-0064 Revision 2: replace per-op-type calibration table with a structural formula `FIXED_PER_CMD + cmd.logical_bytes × R`, defaults anchored at typical composite ≈ 45 ns. Topology yaml override under `pe_cost_model:` block; logical_bytes property per PE command. ADR-0065: implement ADR-0060 §5.6 / §8 item 4 carve-out as a flat-ops CompositeCmd (no head/epilogue structural fields — position + scope drives placement) + first stateful recipe `softmax_merge` (MATH-only 8-step). RECIPE_DESCRIPTORS lives in TLContext-adjacent module only; PE_SCHEDULER stays recipe-free and auto-inserts DMAs from operand `space`. Strict-FIFO RW hazard tracker; ≤1 GEMM per composite invariant. User-facing `tl.composite(prologue=[...], op=, epilogue=[...])` API preserved; existing benches unchanged (meaning-preserving refactor). DDD-0065: implementation-ready phased plan (P0=ADR-0064 Rev2 first, P1-P6 for ADR-0065), file plan, recipe engine sequence, scheduler plan-gen algorithm, RW tracker design, test matrix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,166 +1,242 @@
|
||||
# ADR-0064: op 종류별 CPU 발행 비용 모델 (command construct + dispatch)
|
||||
# ADR-0064: 구조적 CPU dispatch cost 모델 (`logical_bytes` + FIXED + R)
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
Proposed (Revision 2)
|
||||
|
||||
> **ADR-0060**(AHBM GQA Fused Attention)의 보조 ADR. 거기서의 하이브리드
|
||||
> 결정(GEMM은 `tl.composite`, softmax 머지는 커널)이 이기는 이유는
|
||||
> **타일링을 PE_SCHEDULER에 offload하여 CPU가 coarse descriptor를 내리고
|
||||
> 앞서 나가 엔진을 saturate**하기 때문이다. 그 이점이 현재 시뮬레이터에서는
|
||||
> per-op CPU 발행 비용이 0이라 **보이지 않는다**. 본 ADR은 발행 비용을
|
||||
> 실재화하고 **op 종류별로 차등**화하여, composite-vs-primitive 트레이드오프
|
||||
> (그리고 CPU saturation)가 측정 가능하도록 한다.
|
||||
> **ADR-0060** (AHBM GQA Fused Attention) 와 **ADR-0065** (flat-ops
|
||||
> composite + 첫 stateful recipe) 의 보조 ADR. 그 hybrid 의 핵심 — "GEMM 은
|
||||
> `tl.composite` 로, softmax merge 는 커널에서" — 은 **PE_SCHEDULER 로
|
||||
> tiling 을 offload 해서 CPU 가 굵은 descriptor 만 issue 하고 앞서가며
|
||||
> 엔진을 포화시킴** 으로 이깁니다. 그 win 이 현재 시뮬레이터에선 보이지
|
||||
> 않습니다 (per-op CPU issue cost = 0).
|
||||
>
|
||||
> Revision 2 는 원안의 **op-type calibration 표** 를 **logical_bytes 기반
|
||||
> 구조 공식** 으로 대체합니다 — op-type 별 calibration 불필요, 새 op_kind
|
||||
> 도 자동 적용.
|
||||
|
||||
## Context
|
||||
|
||||
### 현재 상태
|
||||
### 현재 코드
|
||||
|
||||
- 모든 `tl.*` op은 커맨드를 emit하기 전에 `_emit_dispatch_overhead()`를
|
||||
호출하며(`tl_context.py:123-125, 190, 227, 235, 612, …`), 이는
|
||||
`dispatch_cycles > 0`일 때**만** `PeCpuOverheadCmd(cycles=dispatch_cycles)`를
|
||||
emit한다. 즉 발행 비용은 `tl.load`, `tl.dot`, MATH op, `tl.composite`에
|
||||
동일하게 적용되는 **단일 균일 노브**다.
|
||||
- 그 노브는 두 실행 경로 모두에서 **0**으로 하드코딩되어 있다
|
||||
(`pe_cpu.py:101` greenlet runner, `:195` legacy replay). ⇒ 커맨드 발행
|
||||
— *descriptor를 construct하고 scheduler 큐에 push* — 이 현재 PE_CPU에서
|
||||
**0 ns**다.
|
||||
- `PeCpuOverheadCmd`는 PE_CPU(`kernel_runner.py:131-132`)와
|
||||
PE_SCHEDULER(`pe_scheduler.py:97-100`)에서 `yield env.timeout(cmd.cycles)`로
|
||||
소비된다. 수동 `tl.cycles(n)`도 존재한다(`tl_context.py:695`).
|
||||
- 모든 `tl.*` op 가 cmd emit 전에 `_emit_dispatch_overhead()` 호출
|
||||
(`tl_context.py:196-212`) → `dispatch_cycles > 0` 일 때만
|
||||
`PeCpuOverheadCmd(cycles=dispatch_cycles)` 발행. knob 은 op kind 무관
|
||||
**uniform** 이며 두 실행 경로 모두 **0** 하드코딩
|
||||
(`pe_cpu.py:101` greenlet, `:195` replay).
|
||||
- ⇒ 명령 issue (descriptor 구성 + scheduler 큐 push) 가 PE_CPU 에서
|
||||
**0 ns** 비용.
|
||||
- `PeCpuOverheadCmd` 는 PE_CPU 에서 `yield env.timeout(cmd.cycles)` 로
|
||||
소비 (`kernel_runner.py:131-132`).
|
||||
|
||||
### 본 ADR을 규정하는 두 가지 발견 (ADR-0060 검토에서)
|
||||
### uniform-zero 가 hybrid 에 부적합한 이유
|
||||
|
||||
- **Q1 — composite 발행 비용.** `tl.composite`를 construct + push 하는 비용은
|
||||
CPU 시간 기준 **~40 ns** 정도(descriptor 빌드 + 큐 push)로 예상된다. 현재는
|
||||
**0**이다. 훅은 존재하며, 값(그리고 op 종류별 차등)만 빠져 있다.
|
||||
- **Q2 — scheduler dispatch vs DMA latency.** PE_SCHEDULER의 composite
|
||||
dispatch는 **non-blocking**이다: `_dispatch_composite`는 tile plan을 생성하고
|
||||
feeder에 enqueue한 뒤 즉시 반환한다(`pe_scheduler.py:104-121`). 실제 **DMA
|
||||
latency는 타일이 흐를 때 PE_DMA에서 부과**되며(`drain_ns =
|
||||
compute_drain_ns(path, nbytes)`, `pe_dma.py:89`), scheduler dispatch에
|
||||
덩어리로 들어가지 **않는다** ⇒ **중복 계상 없음**, DMA는 modeled component에
|
||||
머문다(SPEC §0.1). scheduler의 plan 생성 자체는 현재 sim 시간 **0**이다.
|
||||
ADR-0060 §1 의 핵심: composite 1 개가 `N_tiles` 분량의 GEMM tiling 을
|
||||
offload → CPU 는 `O(1)` 개의 굵은 cmd 만 issue, primitive 경로는
|
||||
`O(N_tiles × ops/tile)`. issue cost = 0 이면 모델은:
|
||||
- primitive 경로의 *CPU 가 push 를 못 따라가 엔진 idle* 을 못 보임
|
||||
- composite 의 *구성 비용 ≫ primitive 1 개이지만 ≪ 대체된 primitive 들의 합* 을 못 보임
|
||||
|
||||
### 균일-그리고-0이 하이브리드에 틀린 이유
|
||||
### Revision 1 의 op-type calibration 이 과한 이유
|
||||
|
||||
하이브리드의 논거 전체는 **하나의** composite descriptor가 `N_tiles`만큼의
|
||||
GEMM 타일링을 offload하므로, CPU가 `O(N_tiles × ops/tile)`개의 fine 커맨드
|
||||
대신 `O(1)`개의 coarse 커맨드를 낸다는 것이다. 발행 비용 = 0(그리고 균일)이면
|
||||
모델은 다음을 보여줄 수 없다:
|
||||
- CPU가 충분히 빨리 못 밀어넣을 때 primitive 경로가 엔진을 **saturate 못 할 수
|
||||
있음**(핵심 ADR-0060 §1 주장), 그리고
|
||||
- composite가 단일 primitive보다 construct 비용이 **더 크지만** 그것이 대체하는
|
||||
다수의 primitive보다는 훨씬 작음.
|
||||
원안은 `cost_table[kind]` (kind = `composite`, `load`, `dot`, `math`, …)
|
||||
형태였습니다. 비용:
|
||||
- kind 마다 값 필요 (calibration ≥ |kinds|)
|
||||
- 새 kind 추가 시마다 entry 추가
|
||||
- 그런데 잡으려던 *ratio* — "composite ≫ primitive, ≪ 대체된 primitive 들의 합" —
|
||||
는 *op kind* 가 아니라 *cmd 가 들고 있는 필드 수* 의 함수.
|
||||
|
||||
단일 균일 `dispatch_cycles`로는 이를 표현할 수 없다: 실제 construct 비용에서
|
||||
`tl.load` ≪ `tl.composite`이기 때문이다.
|
||||
cmd 의 *byte 발자국* 이 자연 proxy: N OpSpec composite 는 1 OpSpec primitive 의 ~N 배 bytes.
|
||||
*고정* 부분 (큐 tail 업데이트, completion 등록, MMIO-급 latency) 은 cmd 마다.
|
||||
둘 합치면: `FIXED + bytes × R`.
|
||||
|
||||
## Decision
|
||||
|
||||
### D1. PE_CPU의 op 종류별 발행 비용 테이블
|
||||
### D1. 구조적 dispatch cost 공식
|
||||
|
||||
단일 `dispatch_cycles` 스칼라를 **커맨드 종류별 비용 테이블**로 교체하고,
|
||||
발행 시점(커맨드가 scheduler로 dispatch되기 전)에 PE_CPU에서 부과한다.
|
||||
greenlet이 이 시간을 지불하며, 이것이 바로 CPU가 얼마나 빨리 work를 밀어넣을
|
||||
수 있는지를 gate한다 — saturation 레버.
|
||||
PE_SCHEDULER 로 가는 모든 PE command 가 PE_CPU 에서 dispatch cycles 소모:
|
||||
|
||||
시작 추정치(추후 calibrate — 검토 항목 참조):
|
||||
```
|
||||
dispatch_cycles(cmd) = FIXED_PER_CMD + cmd.logical_bytes × R
|
||||
```
|
||||
|
||||
| 발행 op | CPU 발행 비용 (construct + push) |
|
||||
- `FIXED_PER_CMD` (cycles/cmd): 큐 tail 업데이트, MMIO-급 RTT,
|
||||
completion-event 등록 — cmd 크기와 무관 고정 비용.
|
||||
- `R` (cycles/byte): scheduler 큐에 cmd 를 직렬화하는 큐-write 대역폭.
|
||||
- `cmd.logical_bytes` (int): cmd 의 *HW-논리* byte 수 — D2 룰로 계산,
|
||||
Python `sys.getsizeof` 가 아님.
|
||||
|
||||
PE_CPU 는 기존 hook 그대로 `PeCpuOverheadCmd(cycles=dispatch_cycles(cmd))`
|
||||
를 dispatch 전에 발행 — 사이클 값만 변경.
|
||||
|
||||
### D2. `logical_bytes` 룰
|
||||
|
||||
각 PE command dataclass 가 `logical_bytes: int` (property) 노출. 룰
|
||||
(HW 친화적, Python overhead 무시):
|
||||
|
||||
| 필드 종류 | bytes |
|
||||
|---|---|
|
||||
| `tl.composite` (GEMM/MATH descriptor) | ~40 ns (Q1 추정) |
|
||||
| `tl.load` / `tl.store` (DMA descriptor) | 작음 (수 ns) |
|
||||
| `tl.dot` / MATH / IPCQ send/recv | 작음 (수 ns) |
|
||||
| cmd framing (cmd 종류 discriminator + completion id 참조) | 4 |
|
||||
| Opcode (op kind enum) | 1 |
|
||||
| Enum (scope 등) | 1 |
|
||||
| `TensorHandle` 참조 (address only — shape/dtype 은 descriptor table 가정) | 8 |
|
||||
| Scalar (int/float) | 4 |
|
||||
| Tuple length marker | 1 |
|
||||
|
||||
요점은 **비율**이다: composite construct ≫ primitive 발행, 그러나 그것이
|
||||
대체하는 primitive 발행들의 합 ≪. 정확한 ns는 설정 가능하다.
|
||||
`CompositeCmd` 는 `ops` 와 `rw_handles` 재귀 합산:
|
||||
|
||||
### D2. 실행 latency는 엔진에 유지 (Q2 — 변경 없음)
|
||||
```python
|
||||
@property
|
||||
def logical_bytes(self) -> int:
|
||||
return (
|
||||
4 # framing
|
||||
+ 1 + sum(op.logical_bytes for op in self.ops)
|
||||
+ 1 + 8 * len(self.rw_handles)
|
||||
)
|
||||
```
|
||||
|
||||
DMA/GEMM/MATH 실행 latency는 오늘처럼 PE_DMA / PE_GEMM / PE_MATH에서 부과된다.
|
||||
발행 비용(D1)은 **CPU 측에만** 추가되며, `drain_ns`를 건드리지 않으므로 중복
|
||||
계상이 없다. 이는 SPEC §0.1(latency는 modeled component에서)을 보존한다.
|
||||
`OpSpec`:
|
||||
|
||||
### D3. scheduler plan 생성 비용 — 0에서 시작, 재검토
|
||||
```python
|
||||
@property
|
||||
def logical_bytes(self) -> int:
|
||||
return (
|
||||
1 + 1 # opcode + scope
|
||||
+ 1 + 8 * len(self.operands) # named operand handles
|
||||
+ (8 if self.out is not None else 0) # out handle
|
||||
+ 1 + sum(4 for _ in self.extra.values()) # extra scalars
|
||||
)
|
||||
```
|
||||
|
||||
PE_SCHEDULER의 tile-plan 생성은 오늘 sim 시간 0이다. 일단 유지한다(지배적
|
||||
레버는 CPU 발행 비용 D1); scheduler 측 비용이 유의미하다고 판명되면 기존
|
||||
`overhead_ns` node attr로 노출한다. 여기서 결정하지 않고 검토 항목으로 둔다.
|
||||
(`DmaReadCmd`, `MathCmd` 등도 같은 룰 — dataclass 당 property 1 개,
|
||||
약 3 줄.)
|
||||
|
||||
### D4. 설정 가능한 값; 골든 재생성
|
||||
### D3. Default — 일반 composite ≈ 45 ns 기준
|
||||
|
||||
비용 테이블은 설정 가능하다(per-topology / node attrs, 기본값은 D1 추정치).
|
||||
발행 비용을 0이 아니게 하면 **모든** bench latency가 바뀌므로, 골든 latency를
|
||||
한 번 **재생성**한다 — ADR-0062의 전역 lazy-load 회귀와 동일한 자세의 모델
|
||||
충실도 개선이다.
|
||||
anchor: 일반 1-op DMA→GEMM→DMA composite 의
|
||||
`logical_bytes ≈ 52` (framing 4 + GEMM OpSpec 39 + rw_handles 9). dispatch
|
||||
목표 45 ns. on-die producer→consumer 큐 4 bytes/cycle 가정.
|
||||
|
||||
```
|
||||
clock = 1 GHz # 1 cycle = 1 ns
|
||||
FIXED_PER_CMD = 32 cycles
|
||||
R = 0.25 cycles/byte
|
||||
```
|
||||
|
||||
검증: `32 + 52 × 0.25 = 45 cycles ≈ 45 ns` ✓
|
||||
|
||||
### D4. topology config override
|
||||
|
||||
default 는 `pe_cpu.py` 에 내장. topology yaml 의 PE 노드 attrs 아래
|
||||
`pe_cost_model:` 절로 override:
|
||||
|
||||
```yaml
|
||||
pe:
|
||||
attrs:
|
||||
pe_cost_model:
|
||||
fixed_per_cmd_cycles: 32
|
||||
byte_cycles_recip: 0.25
|
||||
clock_freq_ghz: 1.0
|
||||
```
|
||||
|
||||
누락된 키는 default. dispatch 공식은 PE_CPU init 시
|
||||
`node.attrs["pe_cost_model"]` 에서 읽음.
|
||||
|
||||
### D5. Scope — 무엇이 cost 를 내고 무엇이 안 내나
|
||||
|
||||
| 경로 | dispatch cost? |
|
||||
|---|---|
|
||||
| PE_CPU → PE_SCHEDULER 의 모든 `PeCommand` | **예** |
|
||||
| `PeCpuOverheadCmd` 자체 (이미 cycles 명시) | **아니오** (공식 우회) |
|
||||
| PE_SCHEDULER 가 자동 생성한 Stage (DMA_READ/WRITE/FETCH/STORE) | **아니오** (PE_SCHEDULER 내부) |
|
||||
| 엔진 compute latency (PE_DMA `drain_ns`, GEMM/MATH `_compute_ns`) | **변화 없음** — 엔진에 그대로 (SPEC §0.1) |
|
||||
|
||||
"latency 는 모델링된 컴포넌트에서" invariant 유지 — dispatch cost 는
|
||||
*추가* CPU-side 시간, 엔진 시간에 fold 안 함.
|
||||
|
||||
### D6. 설정 가능 값; goldens 재생성
|
||||
|
||||
issue cost 를 0 → non-zero 로 바꾸면 **모든** bench 의 latency 변화.
|
||||
이 ADR land 시 골든 latency **한 번** 재생성 — ADR-0062 D3 lazy-load 와
|
||||
같은 패턴. 재생성 후 동일 calibration 이 ADR-0065 opt2 측정에 적용.
|
||||
|
||||
## Alternatives
|
||||
|
||||
### A1. 단일 균일 `dispatch_cycles > 0` 유지
|
||||
### A1. Revision 1 의 op-type calibration 표 유지
|
||||
|
||||
기각: op construct 비용은 자릿수 단위로 다르다(`tl.load` vs `tl.composite`).
|
||||
균일값은 primitive를 과대 청구하거나 composite를 과소 청구하며, 어느 쪽이든
|
||||
하이브리드가 의존하는 composite-vs-primitive 트레이드오프를 왜곡한다.
|
||||
기각: calibration 비용이 |kinds| 에 비례, 그리고 표가 잡으려던 *ratio* 는
|
||||
구조적으로 cmd 크기의 함수. 구조 공식은 같은 정성적 동작을 N 개가 아닌
|
||||
2 개의 calibratable 숫자로 달성.
|
||||
|
||||
### A2. 발행 비용을 PE_CPU 대신 PE_SCHEDULER에 부과
|
||||
### A2. byte-only 공식 (FIXED 항 없음)
|
||||
|
||||
기각: saturation 질문은 *"CPU가 엔진을 바쁘게 유지할 만큼 descriptor를 빨리
|
||||
밀어넣을 수 있는가?"*이며 — 이는 **PE_CPU**의 issue-bandwidth 속성이다.
|
||||
scheduler에 부과하면 CPU back-pressure를 모델하지 못한다.
|
||||
기각. FIXED = 0 이면 opt2 (ADR-0065 Option Y) 가 opt3 를 **이기지 못함**
|
||||
— per-tile dispatch *총 bytes* 가 비슷 (opt3 ≈ 232, opt2 ≈ 380); win 은
|
||||
전적으로 *per-cmd fixed cost 횟수 감소* 에서 옴. byte-only 는 모델이
|
||||
보여야 할 신호를 지움.
|
||||
|
||||
### A3. DMA program/setup 시간을 별도의 고정 per-descriptor 비용으로 모델
|
||||
### A3. PE_SCHEDULER 에 dispatch 비용 부과
|
||||
|
||||
실제 HW는 전송 시간과 별개로 DMA descriptor program 비용을 지불한다. 연기:
|
||||
초기에는 descriptor-program 비용을 **발행 op**의 D1 비용에 합친다(DMA를
|
||||
유발하는 `tl.load` / composite). calibration이 유의미함을 보이면 PE_DMA 고정
|
||||
setup으로 분리한다. 검토 항목.
|
||||
기각: saturation 질문은 *"CPU 가 descriptor 를 push 하는 속도가 엔진을
|
||||
계속 바쁘게 할 수 있는가?"* — **PE_CPU** 의 issue-bandwidth 특성.
|
||||
scheduler 에 부과하면 CPU back-pressure 모델링 안 됨.
|
||||
|
||||
### A4. DMA program/setup 시간을 별도 fixed per-descriptor 비용으로 모델
|
||||
|
||||
연기: 처음엔 descriptor-program 비용을 **issuing op 의** dispatch cost 에
|
||||
fold. calibration 이 분리 필요성을 보이면 PE_DMA fixed setup 으로 분리.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
- 하이브리드의 CPU-offload / saturation 이점(ADR-0060 §1)이 구조적일 뿐
|
||||
아니라 **측정 가능**해진다.
|
||||
- composite-vs-primitive 및 타일링 granularity 트레이드오프가 latency에
|
||||
보여 eval 서사를 가능케 한다.
|
||||
- 하드웨어에 더 충실(issue bandwidth는 실제 병목).
|
||||
- hybrid 의 CPU-offload / saturation win (ADR-0060 §1) 이 **측정 가능**,
|
||||
구조적으로 정직한 모델 (calibration 표 없음).
|
||||
- 새 op_kind 추가 (예: ADR-0065 의 `softmax_merge` 8-step recipe) 가
|
||||
*zero cost* — 같은 공식 자동 적용.
|
||||
- 하드웨어에 더 충실 (queue-head MMIO RTT + queue-write 대역폭).
|
||||
|
||||
### Negative
|
||||
- **모든** bench 골든이 이동 → 한 번의 재생성(D4); CI 골든 fixture 갱신.
|
||||
- op 종류별 값은 calibration 필요; ~40 ns composite 수치는 추정이고
|
||||
primitive는 미지정 — 결과는 숫자만큼만 정확하다(calibrate 전까지 절대
|
||||
latency를 과대 주장 금지).
|
||||
- 발행 경로에 비용 테이블 lookup 추가(런타임 영향 미미).
|
||||
- **모든** bench goldens 변화 → 일회성 재생성 (D6); CI 골든 fixtures 업데이트.
|
||||
- 두 calibration knob (FIXED, R) 필요; default 는 문서화된 가정에 기반 —
|
||||
HW reference 없는 동안엔 절대 latency 를 잠정 취급, **ratio** 만 방어.
|
||||
- 각 PE command dataclass 에 작은 `logical_bytes` property 추가.
|
||||
|
||||
## Open review items (자율 결정; 검토 시 수정)
|
||||
## Open review items
|
||||
|
||||
1. **op 종류별 값의 calibration 출처.** composite ≈ 40 ns는 작업 추정치;
|
||||
primitive 발행 비용은 placeholder. *권고:* 문서화된 가정(instruction-issue +
|
||||
큐 push)에서 값을 택하고, 실제 reference가 생기기 전까지 절대 latency를
|
||||
잠정으로 취급; **비율**을 방어 가능하게 유지.
|
||||
2. **scheduler plan-gen 비용 (D3).** *권고:* 초기에는 0 유지; scheduler-bound
|
||||
동작을 보이는 workload가 나오면 `overhead_ns`로 노출.
|
||||
3. **DMA program 시간 (A3).** *권고:* 먼저 발행 op의 비용에 합치고; 필요 시
|
||||
PE_DMA setup으로 분리.
|
||||
4. **테이블 위치.** *권고:* 흩어진 node attr이 아니라, 커맨드 종류로 키잉되고
|
||||
per-topology 오버라이드 가능한 작은 중앙 cost 모듈 — 값을 한 곳에서 검토
|
||||
가능하도록.
|
||||
5. **회귀 롤아웃.** *권고:* lazy-load(ADR-0062)와 본 ADR의 cost model을 한
|
||||
번의 골든 재생성 패스로 함께 도입하여 두 번의 churn을 피함.
|
||||
6. **legacy replay 경로와의 상호작용**(`pe_cpu.py:_execute_legacy`) — greenlet과
|
||||
replay 경로가 동일한 cost 테이블을 읽어 결과가 일치하도록 보장. 검증 요.
|
||||
1. **FIXED 와 R 의 calibration 출처.** default 는 "일반 composite = 45 ns
|
||||
+ on-die 큐 4 bytes/cycle"; on-die producer→consumer 큐로 합리.
|
||||
HW reference 등장 시 재방문.
|
||||
2. **Scheduler plan-gen 비용.** 0 유지 — D5 가 PE_SCHEDULER 의 plan
|
||||
생성을 dispatch 공식 밖에 둠. scheduler-bound 행동이 보이면 기존
|
||||
`overhead_ns` 로 노출.
|
||||
3. **Override 위치.** topology yaml 의 PE 노드 attrs 아래 `pe_cost_model:`
|
||||
block — 모든 knob 을 한 곳에, 리뷰 가능.
|
||||
4. **경로 parity.** greenlet (`_execute_legacy`, `kernel_runner`) 와
|
||||
replay 둘 다 같은 cost model 읽어야 함. 검증.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
1. **composite는 한 번, primitive는 op마다 청구.** `N_tiles`에 걸쳐 하나의
|
||||
`tl.composite`를 내는 커널은 PE_CPU에서 composite 발행 비용을 한 번 청구하고;
|
||||
동등한 `N_tiles × ops` primitive 커널은 op당 비용을 `N_tiles × ops`번 청구.
|
||||
PE_CPU busy time이 그에 따라 다름을 assert.
|
||||
2. **DMA latency 불변 (Q2).** 고정된 전송에 대해 PE_DMA `drain_ns`는 ADR 이전
|
||||
값과 동일 — 발행 비용은 DMA에 합쳐지지 않고 PE_CPU에 가산(중복 계상 없음).
|
||||
3. **saturation 관측 가능.** per-op 발행 비용이 0이 아니면, many-tile primitive
|
||||
sweep는 GEMM 엔진 idle(CPU-bound 발행)을 보이고 composite sweep는 바쁘게
|
||||
유지 — ADR-0060 §1 레버.
|
||||
4. **결정성:** 동일 입력 → 동일 op_log + latency (SPEC §0.1).
|
||||
5. **경로 일치:** greenlet과 legacy-replay 경로가 동일 커널에 대해 동일한 발행
|
||||
비용 회계를 산출.
|
||||
1. **Anchor 보존.** 일반 DMA→GEMM→DMA composite (1 op,
|
||||
`logical_bytes ≈ 52`) 가 default 값에서 45 ns 에 dispatch.
|
||||
2. **구조적 ratio.** opt3 vs opt2 dispatch (ADR-0065 §verification 의):
|
||||
default calibration 에서 `opt3 / opt2 ≈ 2.4×`.
|
||||
3. **Override 경로.** topology yaml 의 `pe_cost_model:` block 이 per-PE
|
||||
dispatch cost 변경; block 누락 시 default 복귀.
|
||||
4. **`PeCpuOverheadCmd` 우회.** 수동 `tl.cycles(n)` 는 정확히 `n` cycles,
|
||||
`n + dispatch_cycles(...)` 아님.
|
||||
5. **double-count 없음.** PE_DMA `drain_ns`, PE_GEMM/MATH `_compute_ns` 가
|
||||
pre-ADR 값과 동일.
|
||||
6. **결정성.** 동일 입력 → 동일 op_log + latency (SPEC §0.1).
|
||||
7. **경로 parity.** greenlet 과 replay 가 동일 커널에 대해 동일
|
||||
dispatch-cycle 회계.
|
||||
|
||||
## Migration
|
||||
|
||||
ADR-0064 Revision 2 는 단일 PR 로 land:
|
||||
- 각 `PeCommand` dataclass 의 `logical_bytes` property
|
||||
- `pe_cpu.py` dispatch 경로의 공식 적용
|
||||
- PE_CPU init 의 `pe_cost_model:` override read
|
||||
- 일회성 골든 재생성
|
||||
|
||||
land 후 ADR-0065 가 위에 쌓임 — 기존 bench 추가 골든 churn 없음
|
||||
(ADR-0065 는 기존 경로의 `CompositeCmd` 의미 보존 refactor; opt2 만 새 bench).
|
||||
|
||||
Reference in New Issue
Block a user