# ADR-0044: GEMM 평가 하니스 — `scripts/gemm_sweep.py` + `tests/gemm/`

## Status

Accepted

GEMM 평가/특성화 하니스를 문서화한다; 구현과 대조 검증 완료
(상수, tile 크기, figure 집합, script↔test 분할을 교차 확인). D5/D6
caveat은 부정확이 아니라 기록된 한계다.

**ADR-0054로 개정됨**: sweep + renderer가 `milestone-1h-gemm` bench(단일
home)로 이동했다; `scripts/gemm_sweep.py`와 `tests/gemm/`는 이제 거기서
re-export한다. D1/D2의 "데이터 생성은 수동 script / 무거운 작업은 opt-in"은
평가-bench 패턴으로 대체된다(하나의 bench가 전부 재생성;
`MILESTONE_FAST=1`은 committed JSON 재사용).

## Context

ADR-0014(PE pipeline)와 ADR-0042(tile-plan generator)는 GEMM *구현*을
정의하고, ADR-0033은 latency 모델을 정의한다. 그러나 어느 것도 **GEMM
성능을 어떻게 스윕하고 특성화하는가** — 타이밍 데이터를 만드는 shape/variant
스윕과 이를 해석하는 figure — 는 기술하지 않는다. 본 ADR이 그 하니스를
고정한다.

allreduce 하니스(ADR-0043)와 달리 GEMM 스윕은 **무겁다**(24 sim 실행:
8 shape × 3 operand-staging variant; `512` shape 하나가 2048 tile). 이
무게가 아래 분할을 결정한다.

## Decision

### D1. 두 계층 분할 — 무거운 데이터 생성(script) vs. 빠른 figure(test)

- **데이터 생성은 수동 script로 유지**: `scripts/gemm_sweep.py`가
  `matmul-composite`(ADR-0042 plan)를 CLI와 동일한 `run_bench` 경로로
  shape × variant에 걸쳐 실행하고, `result.engine.op_log`를 수확하여
  `docs/diagrams/gemm_sweep.json`(stage별/engine별 wall-clock + occupancy
  + record count + pe/composite window)을 쓴다.
- **figure 렌더링은 test 생성**: `tests/gemm/`이 committed `gemm_sweep.json`을
  읽어 matplotlib PNG를 `docs/diagrams/gemm_plots/`에 렌더링한다. 이
  테스트는 빠르고 기본 실행된다.

근거: 슬라이드덱 규모의 sim 스윕은 매 `pytest` 실행에 속하지 않지만,
figure(저렴·결정적)는 자유롭게 재생성되고 CI로 가드되어야 한다. 이는
CLAUDE.md의 script-vs-test 분할(무거운/수동 생성은 script; 빠른 assertion은
test)을 반영한다.

### D2. Slow regenerator 테스트가 script를 감싼다

`tests/gemm/test_gemm_sweep.py`는 `@pytest.mark.slow`로 표시된다(기본
`addopts: -m "not slow"`에서 제외). 이는 `scripts/gemm_sweep.py`를
subprocess로 호출하여 `gemm_sweep.json`을 on-demand로 재생성한다
(`pytest -m slow tests/gemm/test_gemm_sweep.py`). 스윕 로직은 단일
home(script)을 가지며 테스트는 이를 감싸기만 하므로 sim 구동 코드의
중복이 없다.

### D3. Figure 집합 (3개 차트, `load_ref` variant)

| 테스트 | PNG | 내용 |
|---|---|---|
| `test_plot_gemm_stage_breakdown.py` | `gemm_stage_breakdown.png` | stage별 engine wall-clock (DMA in / Fetch / GEMM / DMA out) |
| `test_plot_gemm_mac_utilization.py` | `gemm_mac_utilization_measured.png` | GEMM util % + useful eff % |
| `test_plot_gemm_mac_utilization.py` | `gemm_mac_utilization_theoretical_vs_measured.png` | theoretical vs 시뮬레이터-measured util/eff |

`tests/gemm/_gemm_plot_helpers.py`가 공유 renderer를 보유한다(시리즈 로직은
`scripts/build_overview_slides.py`의 GEMM `_render_*` 함수를 미러링하며,
그쪽은 여전히 PPTX에 네이티브로 그린다). 수집되지 않음(`test_` 접두사
없음). 각 `test_plot_*`는 `gemm_sweep.json`이 없으면 skip한다.

### D4. Tile 크기는 데이터 기반; under-tile shape는 표시

Tile 크기는 `gemm_sweep.json`(`tile_sizes`)에서 읽으며, 이는 스윕이
`PeSchedulerComponent.TILE_M/K/N = 32/64/32` — 권위 소스 — 에서 기록한
값이다. `M<TILE_M ∨ K<TILE_K ∨ N<TILE_N`인 shape는 차트에
("under-tile") 표시된다. `512³` shape는 figure에서 제외된다
(`EXCLUDED_SHAPES`).

### D5. Theoretical 모델 — 상속된 상수, 아직 ADR-미검증

"theoretical" 곡선은 `scripts/build_overview_slides.py`에서 그대로 복사한
상수로 해석적 ideal-pipeline 모델을 사용한다:

```
HBM_GBS = 256.0   # GB/s        T_STAGE = 16.0 ns
D_STAGES = 3                    BPE = 2
```

**이 값들은 아직 ADR과 대조 소싱되지 않았다.** 특히 ADR-0033의 `256`은
`burst_bytes`(256 B)로 이 `256 GB/s`와 *다른* 양이며, ADR-0033은
대역폭을 `pc_bw_gbs = hbm_to_router_bw_gbs / num_pcs`로 도출한다.
`T_STAGE`/stage 수도 여기서 ADR-0014로 추적되지 않았다. 따라서 모델은
**기존 deck script와 일관할 뿐 ADR과 검증되지 않았고**, 상수가 중복된다
(deck + helper). 이를 조정(topology/ADR-0033/0014에서 소싱, 중복 제거)하는
것은 보류 — Open questions 참조.

### D6. 알려진 네이밍 caveat — `_measured` 차트

`gemm_mac_utilization_measured.png`는 현재 *theoretical* ideal-pipeline
수치를 그린다(footnote가 그렇게 명시). 파일명만 "measured"라고 한다. 이는
그 내용을 시뮬레이터-measured 시리즈로 재지정할지 또는 제목을 바꿀지
결정을 보류 중인 알려진 misnomer다.

## Consequences

### Positive

- GEMM figure가 allreduce처럼 test 생성·CI 가드된다.
- 무거운 스윕은 opt-in으로 유지되어 기본 테스트 실행이 빠르다.
- 스윕 로직의 단일 소스(script)를 slow 테스트가 재사용.

### Negative / limitations

- theoretical 모델 상수(D5)는 미검증·중복이다.
- `_measured` figure는 misnomer(D6).
- `build_overview_slides.py`는 여전히 이 PNG를 임베드하지 않고
  `gemm_sweep.json`에서 GEMM 막대를 네이티브로 그린다 — test 아티팩트를
  소비하도록 deck를 재배선하는 작업은 미완.

## Dependencies

- **ADR-0013**: verification strategy.
- **ADR-0014 / ADR-0042**: PE pipeline + tile-plan generator — 스윕이
  측정하는 GEMM 구현; D4의 stage record count는 ADR-0042 D2/D3에서 온다.
- **ADR-0033**: latency 모델 — D5 상수가 (아직은 아니지만) 추적되어야 할
  소스.
- **ADR-0043**: 형제 격인 allreduce 평가 하니스.

## Open questions

- D5 상수를 `topology.yaml` / ADR-0033 / ADR-0014와 대조 조정하고
  중복 제거할 것인가(모델 파라미터의 단일 소스)?
- D6 `_measured` 네이밍 해결(내용 재지정 vs. 제목 변경)?
- `build_overview_slides.py`를 네이티브 막대 그리기 대신 `gemm_plots/`
  PNG 임베드로 재배선할 것인가?
