Files
Aislo/docs/raw/plans/2026-09-07_speed_watershed_shelved.md
T
eomsangdonandClaude Opus 5 eb30b774f8 chore(docs): docs 폴더 git 추적 전환 · PLAN·OWNERS 최상위 이관
- `docs/` 를 .gitignore 에서 빼 git 추적 대상으로 전환 (위키·완료 이력·검증 기록)
- `docs/raw/PLAN.md` · `docs/raw/OWNERS.md` 를 저장소 최상위로 이동 후 .gitignore 에 등록
  — 창끼리 공유하되 저장소에는 안 올리는 장부
- 살아 있는 경로 참조 7개 파일 정정 (아카이브 107건은 그때 사실이라 그대로 둠)
- graphify 날짜별 산출물(`docs/wiki/graphify-out/20*/`) 제외 — `graphify update` 가 다시 만듦

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 18:19:07 +09:00

145 lines
13 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.
# 유역 속도 개선 — 안 하기로 한 갈래 셋 (보관본)
**2026-09-07 사용자 판단으로 종료** — 「속도개선은 이제 안해도 될것같아. 충분히 빨라.
안한건 계획서에 삭제하고 항목 완료처리하자. 필요시 나중에 다시 하겠음.」
[확인] 한 번이 **293초 → 약 90초**가 된 상태에서 멈춘 것이며, 아래 셋은 **그 뒤로 더 줄이려던
갈래**다. 설계·실측이 다 되어 있으므로 다시 꺼낼 때 여기서부터 이어가면 된다.
계획서(`docs/raw/PLAN.md`) 0-11 에서 이리로 옮겼다 — 계획서에는 실측 표만 남겼다.
---
- [ ] (보류) 🔴 **다음 병목 — `expand_by_red_boundary`**. 2026-09-07 사용자가 목표를
구조물·계획노선 쪽으로 돌려 **유역 속도 작업은 여기서 멈춤**. 아래는 그때까지의 기록.
원래 102초(전체의 85%)였고 EDT 타일·등고선 캐시로 줄인 상태임. 확장 9회, 최종 셀
1,352,325개.
- **9회 중 7회가 격자를 넓힘** (로그 실측) — 1·2회차만 여유(`_pad_spec`, band_cells×2 =
100칸) 안에서 끝나고, 3회차부터 매번 `band_cells`(50칸)씩 커짐:
745×1035 → 795 → 845×1085 → 895×1135 → 945×1185 → 995×1235 → 1045 → 1095×1235.
격자가 커지면 `descent` 재사용 조건(`grown_spec == current`, `Expand.py:159`)이 깨져
**`build_contour_descent` 를 7번 통째로 다시 만듦**.
- 그 안은 `distance_transform_edt` 를 **등고 단계마다 한 번씩** 돎(`..._Descent.py:157~167`).
보조 창 계측에 따르면 단계가 **약 100개** — 격자 1,352,325셀 × 100회 = 회차 1번에만
1.35억 셀-연산. 이것이 7번 돎.
- **고칠 길 둘 — 갈림길은 계측이 정함**
ⓐ **EDT 창 좁히기(결과 보존)** — 쓰는 값은 `members`(그 밴드 셀)뿐이고 한 단 아래
등고선은 보통 수십 셀 안에 있음(주곡선 5m·셀 1m). `members` 바운딩박스를 여유만큼
넓힌 창에서만 EDT 를 돌리고, 나온 최대 거리가 여유에 닿으면 그 단계만 전체로 다시 돎
→ **결과가 반드시 같음**. 격자를 안 키우므로 아래 ⓑ 의 위험도 없음.
ⓑ **여유를 처음부터 크게 잡기** — 빠르지만 ⚠ **결과가 미세하게 달라짐**.
`build_contour_descent` 의 EDT 가 격자 전체를 보므로, 격자가 커지면 밖에 있던
등고선이 시야에 들어와 가장자리 셀의 밴드 표고가 바뀜 → 유역 면적·관경이 움직임.
**수량에 닿으므로 사용자 확인 전에는 안 감.**
- **계측으로 갈림길 확정** (2026-09-07, 보조 창 커밋 `5c22c367`) — **ⓐ 가 맞음.**
| 조각 | 초 | [확인] 116초 중 |
|---|---:|---:|
| **밴드별 하강거리 EDT** | **50.4** | **43%** |
| `classify_flow` | 25.7 | 22% |
| 등고선 굽기(`rasterize_contours`) | 16.3 | 14% |
| 1차 영역 | 4.4 | 4% |
| `_prepare` | 4.9 | 4% |
| B06 확정 | 2.5 | 2% |
| 나머지 | 6 | 5% |
방향장 1회 = 6.3s(745×1035) ~ 10.4s(1095×1235)이고 그중 EDT 가 66~75%.
8회 합계 69.4s. **시간은 셀 수가 아니라 격자 bbox 에 선형**(8·9회차는 셀이 거의 안
늘었는데도 13~14초). 재사용과 무관하게 도는 넷은 회차당 2.6s = 10회 25.7s인데
사실상 전부 `classify_flow` 임. 검산 69.4 + 25.7 = 95.1 ≈ 실측 95.8 ✅
- [x] **ⓐ 구현·검증 끝**(보조 창 커밋 `56d5fb26`) — 밴드별 EDT **10.8s → 2.9s(3.7배)**,
**값 완전 동일**.
- 첫 시도(밴드 바운딩박스 하나로 창 씌우기)는 **안 통했음** — `analyze_domain` 이
`build_contour_descent` 에 `domain` 을 안 넘겨 `inside` 가 격자 전체가 되고, 밴드가
격자 전역에 흩어져 바운딩박스가 사실상 격자 전체였음.
- 그래서 **타일로 쪼갬** — 밴드 셀을 타일로 나눠 타일마다 창을 씌우고, 타일별로 같은
폴백 장치(창 최대거리가 여유에 닿으면 그 타일만 전체 격자로). 채택값 **타일 256 · 여유 32**
(폴백 9/91). 근거표는 그 파일 주석에 남김.
- 검증 — 거리·목표행·목표열 91단 전부 `np.array_equal`, 옛 코드를 별도로 적재해
`build_contour_descent` 결과(`band_elevation`·`valid`·`receiver`·`step_length`·
`azimuth`·`levels`) 전부 동일. 시험 `tmp/tests/test_descent_edt_window.py` 11건.
- ⚠ 함정 — `distance_transform_edt(return_indices=True)` 가 주는 좌표는 **창 좌표**라
전체 격자 좌표로 옮겨 적어야 함. 안 하면 시간은 줄고 흐름 경로만 틀어져 **빨라졌다고
착각**하기 쉬움. 그래서 검증 기준을 「배열이 완전히 같은가」로 둠.
- 지금 남은 그림 ([확인] ≈ 100초 기준) — 확장 78~89s(**88%**) 안에서
방향장 재생성 8회 ≈ 50s(등고선 굽기 ≈ 19s · 밴드 EDT ≈ 16s · 나머지 ≈ 15s) ·
`classify_flow` 10회 ≈ 28s. 그 밖 `_prepare` 6s · 1차 영역 2~7s · B06 확정 4.4s · solve 2.4s.
- [ ] (보류) ② **`classify_flow`**(≈28s) — 재사용과 무관하게 회차마다 도는 유일한 큰 조각.
**2026-09-07 사용자가 목표를 구조물·계획노선 쪽으로 돌려 유역 속도 작업은 여기서 멈춤.**
[확인] 이 293초 → **약 90초**가 된 상태로 두고, 더 줄이는 것은 나중에 판단.
- **자리** — `_walk_from`(`..._Watershed_Flow.py:394`)이 셀 1,352,325개를 **파이썬 루프**로
훑음. 셀마다 수신 셀을 따라가는 사슬이라 전체로는 O(N) 이지만 파이썬이라 회차당 2.6초.
- **고칠 법(설계 확정)** — 이건 「함수형 그래프에서 뿌리 찾기」임. 각 셀은 수신 셀 하나만
가리키고, 사슬은 ⓐ 흡수 셀(도로·세류 → 적색) ⓑ 싱크(자기 자신 → 파랑) ⓒ 해석영역 밖
(→ 파랑) 셋 중 하나에서 끝남. 그러니 **포인터 더블링**(`nxt = nxt[nxt]` 를 고정점까지,
약 log₂N ≈ 20회)으로 전 셀의 뿌리를 한 번에 구하고 그 뿌리의 판정을 뿌리면 됨.
numpy 가져오기 20회 × 1.35M ≈ 0.1초 — **25배** 기대.
- **순서 무관함을 확인해 둠** — 겉보기에 순서에 걸리는 곳은 `:430` 의
`if not valid[following] and state[following] != 2` 하나뿐인데, 무효 셀이 `state==2` 가
되는 경우는 **세류 선확정 셀뿐**이고 그건 시작 전에 못박힘. 무효 셀은 `path` 에 절대
안 들어감(들어가기 전에 `state==2` 로 break). 그래서 단계 함수가 **정적**이고 벡터화가
정확함. 순환은 원래 코드도 파랑으로 처리하므로 더블링 반복 상한에 걸린 것을 파랑으로 두면 같음.
- ⚠ **막힌 곳 하나 — 진단 카운터 둘**. `outer_seeds`·`interior_seeds` 는 「1단계(최외곽
출발)가 몇 셀을 판정했나 / 2단계가 몇 셀을 판정했나」인데, 한 번에 푸는 방식에는 단계가
없음. 이 값을 그대로 내려면 「최외곽에서 출발한 사슬이 지나간 셀 집합」이 필요하고,
그건 함수형 그래프에서 **조상 집합 구하기**라 더블링으로는 안 나옴(같은 뿌리·같은 깊이여도
다른 가지일 수 있음). 닫힘 계산은 사슬 길이만큼 반복해야 해서 이득이 사라짐.
- **쓰는 곳 확인** — 두 값은 응답 페이로드와 TS 타입에만 있고 **계산에 쓰는 곳이 없음**
(`B04_PreProcess_Api_Fetch.ts:397`). 순수 진단값임.
- **그래서 결정** — 값을 바꾸는 일이라 **혼자 처리하지 않음**. 할 때는 ① `reaches`·
`analyzed`·`direction` 이 `np.array_equal` 로 같은지 증명하고 ② 두 카운터는 뜻이 바뀜을
사용자에게 알린 뒤(또는 필드를 없애기로 하고) 진행할 것.
- 안 하기로 한 것 ③ 방향장 자체를 옛 격자에서 옮겨 붙이기 — 이득은 가장 크나 코드·위험도
가장 큼. 위 둘을 끝내고 숫자를 다시 본 뒤 판단.
ode_modules orest-road-webapp` → **저장소 루트 자신**
(npm 이 로컬 패키지를 링크하며 만든 것). 리로더가 B07 을 훑다가 그리로 되돌아가
`venv`·`storage` 를 다시 훑고 또 되감겼음.
- 실측 — 감시 폴더를 `rglob` 하면 파일 **486,809개**, 한 바퀴 **1,045초**. PowerShell 로
세면 `config` 4개·`B07` 23개뿐임. **파이썬 `rglob` 은 정션을 따라 들어가고 PowerShell 은
안 따라가서** 눈으로는 안 보였음.
- 갈래 실험 — 리로드 끔 **0%** / 켬 **64%** 로 갈렸음. 「앱이 먹는다」로 두 번 오진한 것은
프로세스 역할이 뒤바뀌어 보였기 때문임(띄운 것은 껍데기, 자식이 리로더, 손자가 서버).
- 고침 — `main.py` 의 `_reload_dirs` 가 ⓐ 소스 폴더만 보고(`351db312`) ⓑ `node_modules` 를
품은 폴더를 빼고(`e7d30327`·`7a1d7db6`) ⓒ **링크(정션) 폴더를 통째로 건너뛰게**(`70b25a5c`).
**CPU 85% → 18%**. ⓒ 로 정션이 또 생겨도 재발 안 함.
- 부작용 — **`main.py` 와 `config/` 를 고치면 자동 리로드가 안 됨.** 손으로 재시작할 것.
- 남은 18% 는 감시 탓으로 보기 어려움(감시 대상 파이썬 143개) — 급하지 않아 남겨 둠.
- 아직 안 한 것: `watchfiles` 설치(이벤트 방식, 거의 0%) — **사용자 허락 대기**.
- 창 둘이 각각 그러니 **기기가 늘 140% 눌린 채**임. 속도 계측이 회차마다 3배씩
흔들린(1차 영역 2.3s vs 29.4s) 진짜 배경으로 보임.
- 확인한 것 — 새로 띄운 직후에도, 몇 분 놀린 뒤에도 **63~74%**로 일정함.
로그는 한 줄도 안 늘어남. 메모리는 260 → 430MB 로 서서히 늚.
- **범인은 감시(리로더)가 아님** — 감시 프로세스는 **0%**, CPU 를 먹는 것은 **앱 프로세스**임
(`Get-NetTCPConnection` 이 가리키는 쪽이 앱 자식임을 부모-자식으로 확인).
- 배경 태스크 셋(세션 정리 1시간 · 리소스 계측 2분 · 임시파일 정리 6시간)은 전부 `sleep`
간격이 길어 후보가 아님.
- 스택을 떠 봤으나(리로드 없이 8099 로 따로 띄워 60초 뒤 `faulthandler`) **스레드 3개뿐이고
메인은 `run_forever` 에서 대기 중**이라 파이썬 층에서는 도는 것이 안 보였음.
→ C 확장이나 이벤트 루프 폴링 쪽일 가능성. `py-spy` 가 있으면 몇 초면 잡힐 자리인데
지금 환경에 없음. **설치 여부는 사용자 판단**.
- [x] 곁가지 하나는 고침(커밋 `351db312` + `7a1d7db6`) — 자동 리로드가 `storage`(27GB)·
`venv`(파이썬 파일만 7,530개)·`node_modules` 까지 0.25초마다 훑고 있었음. 파이썬이 사는
폴더만, 그중에서도 `node_modules` 를 품지 않은 것만 보게 함.
근거 — **루트 전체 훑기는 이 기기에서 300초에도 한 줄을 못 뱉음**(옛 방식이 0.25초
주기를 지킬 수 없었다는 뜻). 다만 기기가 계속 눌려 있어 정확한 전후 수치는 못 냈음.
⚠ 이것이 70% 를 고치지는 **못했음**. 감시 낭비만 걷어낸 것임.
(부작용 — **`main.py` 와 `config/` 를 고칠 때는 손으로 재시작**할 것. 둘 다 자주 고치는
자리가 아님.)
- ⚠ **측정 함정(2026-09-07 실사고)** — 내 8000 이 **옛 코드로 돌고 있었음**. uvicorn
StatReload 가 `..._Watershed_Stream.py` 변경을 안 물어, 고친 뒤에 잰 [확인] 이 **488초**로
나왔음(고치기 전 293초보다도 나쁨). 수정 로그가 한 줄도 안 찍힌 것으로 발각.
**속도를 재기 전에 ① 백엔드를 확실히 재시작하고 ② 그 수정의 로그가 실제로 찍히는지**
먼저 볼 것.
- [ ] (보류) **[확인] 이 유역을 안 기다리게 미루기** — 보조 창이 설계까지 마쳐 둔 안.
재측정이 여전히 느릴 때만 꺼냄. 요지: 기본 관 자리(`_base_pipes`)는 유역 해석과
무관하나 `build_detail`(`common_util_drainage_detail.py:184`)이 `read_road_routing`
없으면 404 라 통째로 막힘 → 관 목록만 담은 **얕은 갈래**를 하나 두고, `_base_pipes` 를
공용으로 옮겨 **한 벌**을 같이 부르게 함. `pipe_points.json` 에 `pending` 표식 하나.
⚠ **위험** — 배경 계산 중 사용자가 관을 만지면 `confirmed_chainages` 갈래를 타
`place_pipes` 를 아예 안 불러 **300m 규칙이 영구 미적용**됨. 막으려면 배경 채우기를
덮어쓰기가 아니라 **합치기**로 해야 함(보조 창 권고). 보충 관이 늘면 측점 재생성·B06
확정이 각각 1회씩 더 필요함.
**조사 결론(2026-09-07)** — 관 자리(`_base_pipes`)는 「도로 × 세류선 교차점」이라 무거운
흐름 해석과 무관함이 확인됨. 유역을 배경으로 미루면 [확인] 이 7초대까지 떨어질 수 있음.
그러나 위 ⚠ 위험 때문에 **지금은 안 감** — 대신 유역 자체를 줄이는 쪽(0-11 ①②)으로 감.
(윗줄과 같은 항목이라 하나로 합침.)
- ⚠ 속도개선 코드(코리도 캐시·묶음쓰기·`recompute_server_side`)는 계측 단계에서 **건드리지 않음**.