Files
kernbench2/docs/adr-ko/ADR-0049-ver-probe-subcommand.md
T
ywkang 9a02955770 adr: add ADR-0046-0049 — close G4 coverage gaps from /report
Documents four cross-cutting surfaces that previously had no ADR backing,
each surfaced as a G4 candidate by /report:

- 0046 prog-tl-context-contract: the kernel-side tl.* API. Enumerates
  all primitives (ref/load/store/dot/composite/math/reduction/IPCQ/...),
  the two execution modes (command-list vs greenlet runner), scratch
  allocator semantics, dispatch-overhead model, and the kernel registry.

- 0047 par-ahbm-ccl-backend: torch.distributed.init_process_group
  (backend="ahbm") install path. world_size priority (algorithm >
  defaults > topology), the 4-step init sequence (load ccl.yaml, import
  algorithm module, derive world_size, install SFR + IPCQ), greenlet-
  local rank registry, all_reduce dispatch via _defer_wait, barrier
  no-op rationale, and the explicit list of unsupported dist.* APIs.

- 0048 mem-allocator-algorithms: VirtualAllocator + PEMemAllocator
  free-list semantics. Offset-keyed first-fit with coalescing, the
  no-validation trust model for free(), HBM/TCM channel separation,
  page-aligned VA allocation, the page_size dual-default
  (VirtualAllocator 2 MiB / _ensure_allocators 4 KiB fallback), and
  one-allocator-per-sub-unit rule.

- 0049 ver-probe-subcommand: kernbench probe traffic-pattern catalog.
  H2D / D2H / PE DMA categories with their exact cube-index choices,
  the 32 KiB reference size, the 5-point utilization sweep, the
  formula vs actual column meanings, automatic invariant checks
  (monotonicity, D2H >= H2D, best < worst), per-case GraphEngine
  isolation, and the human-readable (not machine-parsable) output
  contract.

Bilingual pair verifier passes for all four EN/KO pairs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:25:04 -07:00

232 lines
11 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-0049: `kernbench probe` Subcommand — Traffic-Pattern Verification Harness
## Status
Accepted (2026-05-22).
`probes/probe.py``run_probe(...)` 가 노출하는 traffic-pattern catalog,
formula vs actual 비교, 그리고 monotonicity / D2H≥H2D 같은 invariant
체크의 의미를 명시한다. ADR-0010 (CLI surface) 가 `kernbench probe`
subcommand 를 enumerate 하나, **probe 가 실제로 측정하는 것**과 **어떤
invariant 를 PASS/FAIL 로 판정하는가**는 ADR-level 에 없었다.
## First action (제일 처음에 하는 일)
`run_probe(topology_path, case_filter=None)` 의 첫 4가지 작업:
1. `Path(topology_path).expanduser().resolve()` 로 절대 경로 산출.
2. `load_topology(path)``TopologyGraph` 인스턴스 (그래프 + spec).
3. `_build_edge_map(graph)``{(src, dst): Edge}` 빠른 lookup 테이블.
4. `AddressResolver(graph)` + `PathRouter(graph)` 인스턴스화.
그 다음 `nbytes = 32768` (= 32 KiB, summary table 의 기준 데이터 크기) 와
`show_all = (case_filter is None or case_filter == "all")` 를 설정.
즉, **probe 의 첫 일은 "토폴로지를 한 번 로드하여 edge map / resolver /
router 를 준비하고, 32 KiB 라는 표준 측정 크기를 픽스하는 것"**. 그 이후
H2D → D2H → PE DMA 세 카테고리의 case 들이 각각 별도의 `GraphEngine`
인스턴스에서 실행된다 (case 간 cross-talk 차단).
## Context
`kernbench probe` 는 다음 의도로 도입된 verification 도구다:
- **수동 분석 ground truth**: 실 시뮬레이션 (`kernbench run --bench ...`)
결과의 latency 가 비정상으로 보일 때, 단순 traffic pattern 의 정답을 별도
로 얻어 비교.
- **formula vs actual 비교**: 분석 모델 (wire latency + overhead + drain)
과 시뮬레이션 결과 (`total_ns`) 가 일치하는지 확인. 일치하지 않으면 모델
단순화 가정 (ADR-0033) 어디가 빠진 것인지 단서.
- **monotonicity check**: hop 수가 늘면 latency 가 단조 증가해야 한다는
invariant 의 자동 확인.
- **utilization sweep**: 데이터 크기 (4 KiB ~ 1 MiB) 별 BW 활용률 표.
이 도구의 동작 사양이 ADR-level 에 없으면:
- 다른 형식의 traffic pattern (예: MCpuDma, IPCQ) 을 추가하려는 사람이 기존
카테고리의 표 포맷 / 측정 단위를 일관되게 따르기 어렵다.
- monotonicity 가 무엇을 기준으로 검사되는지 (hop 수? cube 거리? wire
길이?) 모호.
- 32 KiB 라는 기준 크기와 `[4 KiB, 16 KiB, 64 KiB, 256 KiB, 1 MiB]` sweep
의 의미가 코드 grep 으로만 확인 가능.
## Decision
### D1. 세 가지 case category — H2D / D2H / PE DMA
각 category 는 토폴로지 상 별개의 데이터 경로를 가지며, 별도의 summary
table + sweep table + route detail block 으로 출력된다.
- **H2D (Host→Device Write)**: `MemoryWriteMsg(dst_sip=0, dst_cube,
dst_pe=0, pattern="zero")` 가 `pcie_ep → io_cpu → m_cpu → hbm_ctrl` 경로
를 흐른다. cube 인덱스로 hop 수가 증가:
- h2d-1hop: cube=0, hops=1
- h2d-2hop: cube=4, hops=2
- h2d-3hop: cube=8, hops=3
- h2d-4hop: cube=12, hops=4
- **D2H (Device→Host Read)**: `MemoryReadMsg(src_sip=0, src_cube, src_pe=0)`.
forward command path + reverse data path 의 합 latency. 같은 4 hops
카테고리.
- **PE DMA (PE-initiated)**: `PeDmaMsg(src_sip, src_cube, src_pe, dst_pa)`.
5 가지 케이스로 cube/PE 위치 변화:
- pe-local-hbm: same cube, same PE
- pe-same-half-hbm: same cube, different PE (PE 1)
- pe-cross-half-hbm: same cube, far PE (PE 4)
- pe-cross-cube-hbm-best: adjacent cube (cube 1)
- pe-cross-cube-hbm-worst: diagonal far cube (cube 15)
cube 인덱스가 4/8/12 (H2D), 1/4/15 (PE DMA) 같이 의미 있는 이유는
4x4 cube mesh (sip.cube_mesh.w=4, h=4) 에서의 거리 정의 — 추후 cube_mesh
크기 변경 시 이 값들이 같이 갱신되어야 한다.
### D2. 표준 측정 크기 — `nbytes = 32768` (32 KiB)
모든 case 의 summary table 은 `nbytes=32768` 로 한 번 실행한 결과를
보여준다. 32 KiB 가 선택된 이유:
- DMA overhead 와 BW drain 이 한쪽으로 치우치지 않는 적당한 크기.
- 다수 sub-unit (TCM, register file) 의 1회 transfer 단위와 비교 가능.
크기별 utilization 변화는 별도 sweep table 이 보여준다 (D3).
### D3. Utilization sweep — `[4 KiB, 16 KiB, 64 KiB, 256 KiB, 1 MiB]`
`SWEEP_SIZES = [4096, 16384, 65536, 262144, 1048576]`, `SWEEP_LABELS =
["4KB", "16KB", "64KB", "256KB", "1MB"]`. 매 size 마다 다음 공식:
```
drain = nbytes / bottleneck_bw
total = overhead + wire + drain
eff_bw = nbytes / total
util% = eff_bw / bottleneck_bw × 100
```
`bn_bw is None or <= 0` 이면 그 컬럼은 0.0 % 로 출력. 의미: hop 수가 늘
수록 작은 transfer 는 overhead-bound, 큰 transfer 는 drain-bound 가 되는
패턴을 한 표에서 확인.
### D4. 측정 항목 — actual / formula / breakdown
각 case 행에 표시되는 컬럼:
- `Actual` (total_ns): SimPy 실행 결과의 `trace["total_ns"]`.
- `Ovhd`: 경로상 모든 node 의 `node.attrs["overhead_ns"]` 합 (formula
breakdown).
- `Drain`: `nbytes / min(edge.bw_gbs over path)` (formula).
- `Wire`: `Σ edge.distance_mm * (ns_per_mm from spec)`.
- `Ovhd%` / `Drain%`: Ovhd/Drain 이 Actual 에서 차지하는 비율 (formula 의
Wire 는 통상 매우 작아 표시하지 않음).
- `Eff.BW`: `nbytes / total_ns` (실 측정 BW).
- `BN.BW`: bottleneck bandwidth (formula). path 상 모든 edge 의 BW 중 최소.
edge BW 가 없으면 "-".
- `Util%`: `Eff.BW / BN.BW × 100`. 100% 면 single-stream BW upper bound 에
도달.
formula 의 합 (`wire + ovhd + drain`) 과 actual 의 차이가 크면 모델
단순화가 잡지 못하는 요소가 있다는 신호 (ADR-0033 의 가정 점검).
### D5. Invariant 자동 체크 — PASS/FAIL
다음 invariant 들이 자동으로 확인되어 `[v] PASS` / `[x] FAIL` 로 출력:
- **H2D / D2H monotonic increase**: hop 수가 늘면 actual latency 가
단조 증가해야 함. `all(lats[i] < lats[i+1] for ...)`.
- **D2H ≥ H2D**: 같은 hop 인덱스에서 D2H ≥ H2D (D2H 는 forward command
+ reverse data 두 leg 이므로). `all(d2h[i].total >= h2d[i].total)`.
- **PE DMA best < worst**: cross-cube best (adjacent) latency < cross-cube
worst (diagonal) latency.
- **PE DMA local vs remote**: local BN BW vs remote BN BW 의 비교 출력
(PASS/FAIL 이 아닌 정보성).
체크가 FAIL 이면 사람이 즉시 모델/토폴로지 회귀를 인지할 수 있도록 한
줄로 분명하게 출력.
### D6. Route detail — per-hop timestamp trace
summary 와 sweep 표 이후 각 case 의 path 와 per-hop 누적 시간 (
`_hop_timestamps`) 가 별도 섹션에서 출력된다:
- H2D: leg1 (`pcie_ep → io_cpu`) + leg2 (`io_cpu → m_cpu`) + leg3
(`m_cpu → hbm_ctrl`) + per-hop trace.
- D2H: forward (cmd, no data) + reverse (data) trace 분리 표시.
- PE DMA: `pe_dma → router → hbm_ctrl` path + per-hop trace.
각 hop 의 timestamp 는 cumulative `wire_ns + overhead_ns` 누적. terminal
hop 의 annotation 에 `drain:Xns` 가 붙는다. bottleneck edge 는
`<BN:XXGB/s>` 로 표시되어 시각적으로 식별 가능.
### D7. case_filter 인자의 의미
- `None` 또는 `"all"`: 모든 case 실행 (default).
- 다른 문자열: 그 이름과 정확히 일치하는 case 만 실행. 예: `kernbench
probe --case h2d-2hop`.
각 카테고리 안에서 `name != case_filter` 면 skip 되며, 그 카테고리의
monotonicity / D2H≥H2D 비교는 데이터가 1개일 때 자연히 skip 된다.
CLI parser 의 `--case` 기본값은 `"all"`이라 인자 생략 시 전체 실행.
### D8. 매 case 별 fresh GraphEngine
H2D 4개, D2H 4개, PE DMA 5개의 case 가 각각 **새로운 GraphEngine**
인스턴스에서 실행된다 (`engine = GraphEngine(graph)`). 이유:
- case 간 누적 상태 (op_log, completion 추적, allocator 등) 가 cross-talk
하지 않도록 격리.
- 한 case 의 traffic 이 다른 case 의 BW 측정에 영향을 주지 않도록 보장.
이 격리는 probe 의 측정 결과를 **각 case 단독 single-flow** 의 latency 로
해석할 수 있게 한다. multi-flow contention 측정은 별도 도구 (예:
`pe2pe_overview` 플롯, ADR-0033 의 multi-flow merging 모델) 책임.
### D9. 출력 포맷의 안정성
probe 의 stdout 출력은 사람이 읽기 위함이며, 정확한 컬럼 폭/구분자/공백 은
machine-readable contract 가 아니다. 자동화된 도구가 probe 결과를 파싱
하려면 별도 JSON 출력 모드를 추가해야 한다 (현재 미구현).
PASS/FAIL 줄의 `[v]` / `[x]` 접두사는 CI grep 용 anchor 로 안정 보장.
## Alternatives Considered
### A1. Probe 를 별도 bench 로 등록 (`@bench(name="probe")`)
기각. probe 는 bench 가 아니라 verification 도구로 의도된다 — sweep / 분석
용 multi-engine 실행과 invariant PASS/FAIL 출력이 본질이며, ADR-0045 의
"단일 디바이스 + 단일 RuntimeContext" bench 모델과 맞지 않는다.
### A2. monotonicity 위반 시 exit code 1
기각 (현재). 인간 검사 도구 위주로 의도되어 있어 PASS/FAIL 줄을 출력하고
exit 0 로 종료. CI 가 violation 으로 fail 하길 원하면 별도 wrapper 가
`grep "\[x\]"` 결과로 판단하면 됨. 후속으로 strict-mode flag (`--strict`)
도입 가능.
### A3. probe 의 case 정의를 외부 YAML 로
기각 (현재). 8개 case (4 H2D + 4 D2H + 5 PE DMA — 합 13개) 는 코드에
하드코딩되어 있고 의미가 토폴로지 mesh 구조에 단단히 묶여 있다. 외부
YAML 로 옮기면 cube 인덱스의 의미 (4, 8, 12 / 1, 4, 15) 를 별도로 문서화
해야 하므로 응집도 손실. 케이스 추가가 잦아지면 그때 별도 ADR 로 도입.
### A4. multi-flow contention 측정 추가
기각 (probe 범위 밖). D8 에서 명시한 single-flow 격리 모델이 probe 의 핵심
의도. multi-flow contention 은 ADR-0033 latency model 의 다른 영역으로,
별도 도구 또는 별도 case category 로 처리.
## Consequences
- probe 의 case catalog (D1) 와 측정 단위 (D2/D3) 가 ADR-level 에서 명시
되어, 새 traffic 카테고리 추가 시 어떤 표 포맷을 따라야 하는지 분명.
- formula vs actual 의 컬럼 의미 (D4) 가 굳어져, probe 결과를 보고 "왜
Drain% 가 5% 인가 / 70% 인가" 같은 질문을 빠르게 ADR-0033 가정 점검으로
연결 가능.
- invariant 자동 체크 (D5) 가 ADR 에 굳어져, 향후 latency 모델 변경 시
monotonicity / D2H≥H2D 회귀를 probe 가 즉시 잡아낸다는 안전망 정착.
- D8 의 case 간 격리가 명시되어, probe 결과를 single-flow 측정으로 안전
하게 해석 가능. multi-flow 측정이 필요해지면 별도 도구 트랙이 필요함이
분명.
- A2 의 strict-mode flag 가 후속 작업 후보로 기록되어, CI 통합 요구 시
최소 추가 작업으로 도입 가능.