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

13 KiB
Raw Blame History

유역 속도 개선 — 안 하기로 한 갈래 셋 (보관본)

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 가 6675%. 8회 합계 69.4s. 시간은 셀 수가 아니라 격자 bbox 에 선형(8·9회차는 셀이 거의 안 늘었는데도 1314초). 재사용과 무관하게 도는 넷은 회차당 2.6s = 10회 25.7s인데 사실상 전부 classify_flow 임. 검산 69.4 + 25.7 = 95.1 ≈ 실측 95.8 ✅

    • ⓐ 구현·검증 끝(보조 창 커밋 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초 기준) — 확장 7889s(88%) 안에서 방향장 재생성 8회 ≈ 50s(등고선 굽기 ≈ 19s · 밴드 EDT ≈ 16s · 나머지 ≈ 15s) · classify_flow 10회 ≈ 28s. 그 밖 _prepare 6s · 1차 영역 27s · 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 가 있으면 몇 초면 잡힐 자리인데 지금 환경에 없음. 설치 여부는 사용자 판단.
    • 곁가지 하나는 고침(커밋 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)는 계측 단계에서 건드리지 않음.