Files
kernbench2/docs/adr-ko/ADR-0053-dev-topology-builder-algorithms.md
T
ywkang bd49c93703 adr: add ADR-0050-0053 — close /report's second-pass G4 candidates
Documents four cross-cutting surfaces one layer deeper than the prior
G4 batch:

- 0050 par-ccl-algorithm-module-contract: how to author a new CCL
  algorithm in src/kernbench/ccl/algorithms/. Pairs with ADR-0045's
  bench-module contract. Pins the four required public symbols
  (kernel, kernel_args, TOPO_NAME_TO_KIND constants, kernel alias),
  the 9 + tl standardized kernel signature, the kernel_args tuple
  format, sip_topo_kind dispatch, and the ccl.yaml entry workflow.

- 0051 lat-routing-helper-api: every public method of AddressResolver
  (resolve, find_m_cpu, find_pcie_ep, find_io_cpu, find_all_pcie_eps)
  and PathRouter (find_path, find_path_with_distance,
  find_mcpu_dma_path, find_memory_path, find_node_path + 2 shims).
  Pins the four adjacency graphs (_adj_all / _adj / _adj_mcpu_dma /
  _adj_local) and the edge-kind exclusion sets they use, plus the
  single-owner naming convention.

- 0052 dev-oplog-memory-store-schemas: OpRecord's 7 fields, the
  per-op_name params matrix (dma_read, dma_write, gemm_*, math, math
  reduction, composite_gemm, ipcq_copy, unknown), snapshot timing
  rules (math = all inputs, dma_write = HBM-only — ADR-0027 race
  avoidance), TileToken stage_type capture, and MemoryStore's
  (space, addr) two-level dict with reference-store semantics.

- 0053 dev-topology-builder-algorithms: the 6-stage compile pipeline,
  cube_mesh.yaml's source_hash cache and its 5 input fields, the
  cube NoC auto-layout algorithm (row/col placement, HBM exclusion
  zone, PE/M_CPU/SRAM attachment via nearest-router, UCIe N/S/E/W
  distribution), the node naming convention (single-owner with
  router.py), the edge-kind catalog, the 4 view projections, and a
  table of spec-field changes vs mesh regeneration.

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:52:42 -07:00

308 lines
15 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-0053: Topology Builder + Visualizer Algorithms
## Status
Accepted (2026-05-22).
`topology/builder.py`, `topology/mesh_gen.py`, `topology/visualizer.py`
함께 수행하는 토폴로지 컴파일·시각화 파이프라인의 핵심 알고리즘 선택
(placement-driven router attachment, mesh auto-layout, source_hash 캐시,
view projection, SVG rendering) 을 명시한다. ADR-0006 가 topology
compilation 의 high-level intent (compiled topology, distance extraction,
automatic diagram generation) 를 정의하나, **builder 가 실제로 어떤
알고리즘을 사용하는지** 는 코드 grep 으로만 확인 가능했다.
## First action (제일 처음에 하는 일)
`resolve_topology(path_str)` 가 호출되면 다음 4 단계가 순서대로 일어난다:
1. **경로 검증** (`builder.py::resolve_topology`):
`Path(path_str).expanduser().resolve()`, 존재 확인, file 여부 확인.
실패 시 `FileNotFoundError` 또는 `ValueError`.
2. **YAML 파싱** (`_read_spec`): `yaml.safe_load`. parse error 면 line/
column 정보 포함한 `ValueError`. dict 가 아니면 reject.
3. **mesh 자동 생성** (`mesh_gen.ensure_mesh_file`): topology yaml 과
같은 디렉터리에 `cube_mesh.yaml` 을 만들거나 (캐시 invalid 시) 재사용
(캐시 hit 시). 이 단계가 cube NoC 의 라우터 grid 와 부착 정보를 결정.
4. **graph 컴파일** (`_compile_graph`): system → IO chiplets → cubes →
inter-cube edges → IO↔cube edges → system↔IO edges 순으로 nodes/edges
를 누적, 그 다음 4 개의 view projection (system, sip, cube, pe) 을
생성하여 `TopologyGraph` 로 묶음.
즉, **topology compile 의 첫 일은 "topology.yaml 을 dict 로 읽고, 동일
디렉터리에 cube_mesh.yaml 을 생성/검증한 뒤, system→sip→cube→pe 순으로
flat graph + 4-view projection 을 만드는 것"** 이다.
## Context
`topology/` 패키지의 책임:
- **builder.py** (1207 줄): topology.yaml 을 받아 `TopologyGraph` (nodes
+ edges + 4 view projections) 를 컴파일.
- **mesh_gen.py** (305 줄): cube NoC 의 라우터 grid 와 PE/UCIe/M_CPU/SRAM
부착 위치를 자동 결정하여 `cube_mesh.yaml` 로 캐시.
- **visualizer.py** (887 줄): `TopologyGraph` 로부터 SVG 다이어그램 4종
(system / sip / cube / pe) 을 생성.
ADR-0006 가 "topology compilation 의 결과는 distance metadata 와 diagram
generation 의 single source" 라는 high-level 결정을 정의하나, 구체 알고리즘
(예: placement-driven nearest-router attachment, HBM 제외 zone 산출,
source_hash 의 어떤 필드가 invalidation 을 트리거하는가) 은 ADR 에 없다.
특히 다음 결정들이 ADR-level 에 부재:
- 왜 mesh_gen 이 별도 파일 (`cube_mesh.yaml`) 로 캐시되는가?
- source_hash 가 어떤 필드를 포함하며, 어떤 변경이 재생성을 강제하는가?
- placement coordinate 가 cube 좌표가 아닌 mm 단위인 이유?
- HBM zone 제외와 UCIe N/S/E/W 분배가 mesh 안에서 어떻게 결정되는가?
- view projection 4 개 (system/sip/cube/pe) 의 추상화 레벨 차이?
이 ADR 이 이 결정들을 한 곳에 정리한다.
## Decision
### D1. compile 파이프라인 — 6 단계
`_compile_graph(spec)`:
1. **시스템 노드 생성** (`_instantiate_system`): `fabric.switch0`, host CPU
등 system-level 노드 추가.
2. **per-SIP loop** (`for sip_id in range(system.sips.count)`):
- **IO chiplets** (`_instantiate_io_chiplets`): pcie_ep / io_cpu /
io_noc / io_ucie PHY / conn 노드 + 내부 양방향 edge 생성.
- **cube instantiation** (`_instantiate_cube`): cube_mesh.yaml 의 router
grid 를 토대로 cube-별 라우터, PE sub-components (pe_cpu, pe_dma,
pe_fetch_store, pe_gemm, pe_math, pe_mmu, pe_tcm, pe_scheduler,
pe_ipcq), m_cpu, sram, hbm_ctrl 인스턴스화 + 내부 edge 깔기.
- **inter-cube edges** (`_add_inter_cube_edges`): UCIe N/S/E/W mesh
edge.
- **IO ↔ cube edges** (`_add_io_to_cube_edges`): io_noc 와 cube 의
edge UCIe phy 사이 연결.
3. **switch ↔ IO edges** (`_add_system_to_io_edges`): `fabric.switch0`
와 각 SIP 의 `pcie_ep` 사이 양방향 edge (ADR-0038 D3 + ADR-0010 의
cross-SIP IPCQ 경로).
4. **view projections** 4 종 build:
- `_build_system_view(spec)` — Tray 레벨, SIP 들과 system switch.
- `_build_sip_view(spec)` — SIP 안의 cube mesh + IO chiplet.
- `_build_cube_view(spec)` — 단일 cube 안의 router grid + PE/M_CPU/SRAM/
HBM_CTRL 부착.
- `_build_pe_view(spec)` — 단일 PE 안의 9 sub-components + 내부 edge.
5. **TopologyGraph 리턴**: `TopologyGraph(spec, nodes, edges, system_view,
sip_view, cube_view, pe_view)`.
이 6 단계는 **순서가 의미를 가진다**: cubes 가 만들어진 후에야 inter-cube
edges 가 valid 한 src/dst 를 갖고, IO chiplet 이 먼저 만들어져야 IO ↔ cube
edge 가 그를 참조할 수 있다. 새 노드 종류를 끼울 때는 의존 관계를 보고
적절한 위치에 삽입해야 한다.
### D2. `cube_mesh.yaml` — 별도 파일 + source_hash 캐시
`mesh_gen.ensure_mesh_file(cube_spec, mesh_path)`:
1. `source_hash = _compute_source_hash(cube_spec)` 산출. 입력 필드:
- `geometry` (cube_mm.w/h 등).
- `pe_layout` (corners, pe_per_corner).
- `ucie.n_connections`.
- `memory_map.hbm_mapping_mode`.
- `placement` (m_cpu/sram pos_mm).
2. `mesh_path` (= `topology.yaml` 와 같은 디렉터리의 `cube_mesh.yaml`) 이
존재하고 `existing.source_hash == source_hash` 면 재사용 (캐시 hit).
3. 아니면 `_generate_mesh(cube_spec, source_hash)` 로 새 mesh 생성 후
yaml 로 저장.
별도 파일로 캐시하는 이유:
- mesh 생성은 PE/UCIe/router 부착 계산이 들어가 매번 다시 하기 무거움.
- 같은 cube spec 으로 여러 번 실행 시 동일 mesh 가 보장되어야 함.
- 사람이 직접 mesh 를 inspect / debug 할 수 있는 artifact 가 됨.
`source_hash` 가 list 한 5 개 필드가 mesh 형상을 결정하는 핵심이며, 그
외 (예: bandwidth, overhead_ns) 변경은 mesh 재생성을 트리거하지 않는다.
### D3. cube NoC mesh auto-layout 알고리즘
`_generate_mesh(cube_spec)`:
#### D3.1. 행/열 결정
- `pe_positions = _corner_pe_positions(cube_w, cube_h)`: 4 corner (NW/NE/
SW/SE) 마다 PE center 좌표 (mm). hardcoded `(1.5, 1.5)` / `(cube_w-1.5,
cube_h-1.5)` 패턴 + `pe_per_corner=2` 면 각 corner 에 2 PE 위치.
- `col_xs = _compute_col_positions(...)`: PE 들의 x 좌표 union + `max_spacing
= 3.0 mm` 보다 큰 gap 에 relay 컬럼 삽입.
- `row_ys, rows_per_half = _compute_row_positions(cube_h, n_connections,
pe_positions)`:
- `n_conn = max(n_connections, 2)` (hot path minimum).
- `rows_per_half = ceil(n_conn / 2)`.
- top 절반 + HBM 두 row + bottom 절반. HBM 은 `(cube_h/2 - 1.5, cube_h/2
+ 1.5)` 에 위치. PE rows 와 HBM rows 사이 `hbm_gap = 1.5 mm`.
#### D3.2. HBM 제외 zone
`hbm_row_start = rows_per_half`, `hbm_row_end = rows_per_half + 1`.
`hbm_col_start = n_cols // 2 - 1`, `hbm_col_end = n_cols // 2`.
이 (row, col) 사각형 안의 router 슬롯은 `None` 으로 마킹 (라우터 없음).
실제 HBM 컨트롤러는 별도 `hbm_ctrl.pe{X}` 노드로 ADR-0017 D9 의 per-PE
파티션 패턴을 따라 부착.
#### D3.3. PE 부착
각 corner 의 PE 들은 다음 row 에 매핑:
- Top half: NW → row 0, NE → row 1 (top_corners 안의 index).
- Bottom half: SW → row `hbm_row_end + 1`, SE → row `hbm_row_end + 2`.
각 PE 의 x 좌표가 가장 가까운 col 의 router 에 부착 (`min(range(n_cols),
key=lambda c: abs(col_xs[c] - pe_x))`). 부착 항목은 `pe{pe_idx}.dma`,
`pe{pe_idx}.cpu`, `pe{pe_idx}.hbm` 세 가지 (router 별 attach list 에 push).
#### D3.4. M_CPU / SRAM 부착 — nearest router by Euclidean distance
`placement.m_cpu.pos_mm` (default `[1.5, 5.5]`) 와 `placement.sram.pos_mm`
(default `[1.5, 8.5]`) 의 좌표에서 가장 가까운 router 를 Euclidean
distance 로 찾아 attach list 에 `"m_cpu"` / `"sram"` 추가.
#### D3.5. UCIe N/S/E/W 분배
`ucie_pe_rows = top_pe_rows + bot_pe_rows` (총 `2 * rows_per_half` 개).
- UCIe-E: 매 PE row 마다 rightmost col 의 router 에 `ucie_e.c{i}`.
- UCIe-W: leftmost col 의 router 에 `ucie_w.c{i}` (E 의 mirror).
- UCIe-N/S: PE column 들 중 절반을 좌측, 절반을 우측으로 나눠 top row /
bottom row 의 해당 col 에 부착.
각 UCIe connection 은 `c{i}` index 가 붙어 ucie_n_connections 만큼의 PHY
가 분산된다 (ADR-0017 D5+).
### D4. node 명명 규칙 — 단일 소유자
builder.py 는 다음 명명 규칙으로 노드를 만든다 (ADR-0051 D5 의 단일
소유자 원칙):
- `fabric.switch0` — system-level switch.
- `sip{S}.{io_id}.{pcie_ep|io_cpu|io_noc|io_ucie.{dir}|conn.{id}}` — IO
chiplet.
- `sip{S}.cube{C}.{m_cpu|sram|hbm_ctrl.pe{X}|noc.r{R}c{C}|...}` — cube 내부.
- `sip{S}.cube{C}.pe{P}.{pe_cpu|pe_dma|pe_fetch_store|pe_gemm|pe_math|pe_mmu|pe_tcm|pe_scheduler|pe_ipcq}` — PE sub-components.
이 명명 규칙을 변경하려면 builder.py 와 router.py (ADR-0051) 의 helper
양쪽이 함께 갱신되어야 한다. 컴포넌트는 명명 규칙을 직접 알지 못하고
helper 만 호출한다.
### D5. edge `kind` 분류
각 edge 가 부여받는 `kind` 가 라우팅 정책 (ADR-0051 D2) 의 입력. 주요
kind 값:
- `"pe_internal"` — PE 내부 sub-component 간.
- `"pe_to_router"` — PE_DMA ↔ cube NoC router.
- `"router_mesh"` — cube NoC router 간.
- `"router_to_hbm"`, `"router_to_mcpu"`, `"router_to_sram"`,
`"sram_to_router"` 등 — cube-attached component 간.
- `"ucie_internal"`, `"ucie_conn_to_router"`, `"router_to_ucie_conn"`,
`"ucie_conn_to_noc"`, `"noc_to_ucie_conn"`, `"ucie_mesh"` — UCIe 관련.
- `"io_internal"` — IO chiplet 내부.
- `"io_to_cube"`, `"cube_to_io"` — IO ↔ cube 경계.
- `"pcie"` — switch ↔ pcie_ep.
- `"command"` — control-plane only edges (M_CPU ↔ NOC 등; PE DMA path 에서
제외).
새 edge kind 를 추가하면 router.py 의 4 adjacency graph (ADR-0051 D2) 의
어느 카테고리에 속할지 결정해야 한다 — 그렇지 않으면 default 로 `_adj_all`
에만 포함되어 의도와 다른 routing 발생 가능.
### D6. view projection — 4 추상화 레벨
`TopologyGraph` 는 flat (nodes + edges) 외에 4 개의 view projection 을
보유:
- **system_view** (`_build_system_view`): Tray 레벨. SIP 박스들 + `fabric.
switch0`. PCIE 링크 표시. 외부 발표용 high-level overview.
- **sip_view** (`_build_sip_view`): 한 SIP 안. cube mesh + IO chiplet
(pcie_ep + io_cpu + io_noc). UCIe N/S/E/W 가 cube 간 연결로 보임.
- **cube_view** (`_build_cube_view`): 한 cube 안. router grid + PE/M_CPU/
SRAM/HBM_CTRL 부착 + UCIe PHY edge 부분. cube 내부 라우팅 / placement
진단용.
- **pe_view** (`_build_pe_view`): 한 PE 안. 9 sub-components + 내부 edge
(pe_internal kind). 자세한 PE 내부 dataflow 검토용.
view 는 spec 에서 `visualization.emit_views: [system, sip, cube]` 같이
선택적으로 출력 (ADR-0006). pe view 는 기본 출력에서 빠져 있으나 코드는
유지 (자세한 디버그용).
### D7. visualizer.py — SVG 다이어그램 출력
`emit_diagrams(graph, out_dir)` 가 모든 view 를 SVG 로 렌더. 핵심 함수:
- `_render_view_svg(view)` — 일반적인 view 렌더 (router grid 가 없는
경우).
- `_render_cube_view_svg(view, spec)` — cube view 전용 (HBM block 그리기,
router grid layout, PE/M_CPU/SRAM/HBM positioning).
- `_draw_node`, `_draw_edge` — 노드 / edge 의 시각적 표현.
- `_pick_scale`, `_compute_node_sizes` — 자동 스케일링.
visualizer 는 **derived artifact** (ADR-0006) 로 분류되며, 코드 변경 시
production check 대상이 아니다. CLAUDE.md 의 "Derived Artifacts" 항목과
정합.
### D8. spec 변경의 영향 범위
| spec 필드 | 영향 | mesh 재생성 |
|---------------------------------------|-------------------|-------------|
| `system.sips.count` | SIP 갯수, node 수 | No |
| `sip.cube_mesh.w/h` | cube mesh 형상 | No |
| `cube.geometry.cube_mm.w/h` | cube 크기 (mm) | **Yes** |
| `cube.pe_layout.corners/pe_per_corner`| PE 부착 위치 | **Yes** |
| `cube.ucie.n_connections` | UCIe PHY 분배 | **Yes** |
| `cube.memory_map.hbm_mapping_mode` | HBM 분배 모드 | **Yes** |
| `cube.placement` | M_CPU/SRAM 위치 | **Yes** |
| `cube.memory_map.*` (위 제외) | HBM 용량 / BW | No |
| `*.links.*.bw_gbs` | edge bandwidth | No |
| `*.attrs.overhead_ns` | 컴포넌트 latency | No |
위 표가 D2 의 `_compute_source_hash` 입력과 일치. mesh 재생성이 필요한
변경은 `cube_mesh.yaml` 의 source_hash 가 자동 invalidate.
## Alternatives Considered
### A1. mesh 를 별도 캐시 파일 없이 매 compile 시 재생성
기각. 같은 spec 으로 여러 번 호출되는 케이스 (CLI run, probe, test) 마다
mesh 생성 비용을 다시 지불. 또한 사람이 mesh 를 inspect 할 수 있는 artifact
가 사라짐.
### A2. mesh 생성을 builder.py 에 합치기
기각 (현재). 305 줄 짜리 자체 알고리즘이며, mesh layout 의 결정 (placement-
driven router attachment, HBM exclusion zone) 이 builder 의 일반적인
node/edge 생성 책임과 다르다. 분리 유지가 단일 책임 원칙에 더 부합.
### A3. placement coordinate 를 cube 좌표 (col/row) 로 표현
기각. mm 단위 좌표가 시각화 측 (visualizer) 과 mesh layout 측 (nearest-
router 산출) 양쪽에서 일관되게 쓰인다. cube 좌표는 router grid 가 결정
되기 전까지는 정의되지 않으므로 placement 입력에 부적절.
### A4. view projection 을 lazy 하게 생성
기각 (현재). 4 개 view 의 생성 비용이 작고 (보통 < 100 ms), eager 생성이
`TopologyGraph` 를 통한 single source of truth 를 보장.
### A5. visualizer 출력 형식을 SVG 외 (PNG/PDF) 도
기각. SVG 가 vector + 텍스트 검색 가능 + 브라우저 직접 렌더가 가능한 가장
유연한 형식. PNG 변환이 필요하면 별도 도구 (rsvg-convert 등) 로 후처리.
## Consequences
- ADR-0006 의 high-level intent 가 D1D7 로 구체화되어, topology 변경
영향을 D8 표로 빠르게 가늠 가능.
- D3 의 mesh auto-layout 알고리즘이 ADR-level 에서 굳어져, 추후 새 PE
부착 패턴 (예: HBM 의 6-zone 분할) 도입 시 어느 단계가 영향받는지 명확.
- D5 의 edge kind 목록과 D7 의 view 구조가 명시되어, 새 component 종류
추가 시 (builder + router + visualizer) 어디까지 손대야 하는지 PR
reviewer 가 한눈에 파악 가능.
- D2 의 source_hash invalidation 규칙이 명시되어, cube_mesh.yaml 이 stale
하게 남는 경우 (예: bw 값만 바꿨을 때) 가 정상 동작임이 분명.