Files
kernbench2/docs/adr-proposed/ADR-0064-perf-cpu-issue-cost-model-ko.md
T
ywkang 7a9d4ec47b gqa(adr): pivot ADR-0060 to composite hybrid + lazy tl.load; add ADR-0064 cost model
- ADR-0060: GEMMs (Q.Kt, P.V) via existing tl.composite (scheduler-managed
  tiling + K/V DMA streaming); softmax merge + IPCQ tree reduction stay in
  kernel. Front TL;DR pseudocode of the final composite kernel; new section
  B lists open design items (DDD sync, K pre-transpose, dma_read lever,
  kernel-vs-scheduler tiling, ring path).
- ADR-0062: redefined from a new load_async op to global lazy tl.load
  (non-blocking + auto-wait on first use; API unchanged; goldens regenerate).
- ADR-0064 (new): per-op-type CPU issue cost model (composite ~40ns >>
  primitive) so the hybrid's CPU-saturation win becomes measurable
  (currently dispatch_cycles=0 hides it). Cost-model impl deferred.
- KO mirrors for ADR-0060/0062/0064 (-ko suffix, adr-proposed).

Rationale: non-blocking CompositeCmd offloads tiling to PE_SCHEDULER,
decoupling CPU issue-rate from execution so the CPU can saturate the
engines; the prior 'composite = no latency benefit' claim was an artifact
of dispatch_cycles=0. Docs only; no production code changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 10:19:37 -07:00

167 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0064: op 종류별 CPU 발행 비용 모델 (command construct + dispatch)
## Status
Proposed
> **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)가 측정 가능하도록 한다.
## 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`).
### 본 ADR을 규정하는 두 가지 발견 (ADR-0060 검토에서)
- **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**이다.
### 균일-그리고-0이 하이브리드에 틀린 이유
하이브리드의 논거 전체는 **하나의** 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보다는 훨씬 작음.
단일 균일 `dispatch_cycles`로는 이를 표현할 수 없다: 실제 construct 비용에서
`tl.load``tl.composite`이기 때문이다.
## Decision
### D1. PE_CPU의 op 종류별 발행 비용 테이블
단일 `dispatch_cycles` 스칼라를 **커맨드 종류별 비용 테이블**로 교체하고,
발행 시점(커맨드가 scheduler로 dispatch되기 전)에 PE_CPU에서 부과한다.
greenlet이 이 시간을 지불하며, 이것이 바로 CPU가 얼마나 빨리 work를 밀어넣을
수 있는지를 gate한다 — saturation 레버.
시작 추정치(추후 calibrate — 검토 항목 참조):
| 발행 op | CPU 발행 비용 (construct + push) |
|---|---|
| `tl.composite` (GEMM/MATH descriptor) | ~40 ns (Q1 추정) |
| `tl.load` / `tl.store` (DMA descriptor) | 작음 (수 ns) |
| `tl.dot` / MATH / IPCQ send/recv | 작음 (수 ns) |
요점은 **비율**이다: composite construct ≫ primitive 발행, 그러나 그것이
대체하는 primitive 발행들의 합 ≪. 정확한 ns는 설정 가능하다.
### D2. 실행 latency는 엔진에 유지 (Q2 — 변경 없음)
DMA/GEMM/MATH 실행 latency는 오늘처럼 PE_DMA / PE_GEMM / PE_MATH에서 부과된다.
발행 비용(D1)은 **CPU 측에만** 추가되며, `drain_ns`를 건드리지 않으므로 중복
계상이 없다. 이는 SPEC §0.1(latency는 modeled component에서)을 보존한다.
### D3. scheduler plan 생성 비용 — 0에서 시작, 재검토
PE_SCHEDULER의 tile-plan 생성은 오늘 sim 시간 0이다. 일단 유지한다(지배적
레버는 CPU 발행 비용 D1); scheduler 측 비용이 유의미하다고 판명되면 기존
`overhead_ns` node attr로 노출한다. 여기서 결정하지 않고 검토 항목으로 둔다.
### D4. 설정 가능한 값; 골든 재생성
비용 테이블은 설정 가능하다(per-topology / node attrs, 기본값은 D1 추정치).
발행 비용을 0이 아니게 하면 **모든** bench latency가 바뀌므로, 골든 latency를
한 번 **재생성**한다 — ADR-0062의 전역 lazy-load 회귀와 동일한 자세의 모델
충실도 개선이다.
## Alternatives
### A1. 단일 균일 `dispatch_cycles > 0` 유지
기각: op construct 비용은 자릿수 단위로 다르다(`tl.load` vs `tl.composite`).
균일값은 primitive를 과대 청구하거나 composite를 과소 청구하며, 어느 쪽이든
하이브리드가 의존하는 composite-vs-primitive 트레이드오프를 왜곡한다.
### A2. 발행 비용을 PE_CPU 대신 PE_SCHEDULER에 부과
기각: saturation 질문은 *"CPU가 엔진을 바쁘게 유지할 만큼 descriptor를 빨리
밀어넣을 수 있는가?"*이며 — 이는 **PE_CPU**의 issue-bandwidth 속성이다.
scheduler에 부과하면 CPU back-pressure를 모델하지 못한다.
### A3. DMA program/setup 시간을 별도의 고정 per-descriptor 비용으로 모델
실제 HW는 전송 시간과 별개로 DMA descriptor program 비용을 지불한다. 연기:
초기에는 descriptor-program 비용을 **발행 op**의 D1 비용에 합친다(DMA를
유발하는 `tl.load` / composite). calibration이 유의미함을 보이면 PE_DMA 고정
setup으로 분리한다. 검토 항목.
## Consequences
### Positive
- 하이브리드의 CPU-offload / saturation 이점(ADR-0060 §1)이 구조적일 뿐
아니라 **측정 가능**해진다.
- composite-vs-primitive 및 타일링 granularity 트레이드오프가 latency에
보여 eval 서사를 가능케 한다.
- 하드웨어에 더 충실(issue bandwidth는 실제 병목).
### Negative
- **모든** bench 골든이 이동 → 한 번의 재생성(D4); CI 골든 fixture 갱신.
- op 종류별 값은 calibration 필요; ~40 ns composite 수치는 추정이고
primitive는 미지정 — 결과는 숫자만큼만 정확하다(calibrate 전까지 절대
latency를 과대 주장 금지).
- 발행 경로에 비용 테이블 lookup 추가(런타임 영향 미미).
## 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 테이블을 읽어 결과가 일치하도록 보장. 검증 요.
## 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 경로가 동일 커널에 대해 동일한 발행
비용 회계를 산출.