9a02955770
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>
232 lines
11 KiB
Markdown
232 lines
11 KiB
Markdown
# 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 통합 요구 시
|
||
최소 추가 작업으로 도입 가능.
|