From 1553f23c0d9fa2f0f06fd2cbdab9bad290593d8a Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 05:48:38 +0900 Subject: [PATCH] =?UTF-8?q?perf(B04):=20=EB=93=B1=EA=B3=A0=EC=84=A0=20?= =?UTF-8?q?=ED=94=BC=EC=B2=98=20=ED=92=80=EA=B8=B0=EB=A5=BC=20=ED=95=9C=20?= =?UTF-8?q?=EB=B2=88=EB=A7=8C=20(=EA=B2=A9=EC=9E=90=EC=99=80=20=EB=AC=B4?= =?UTF-8?q?=EA=B4=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PLAN 0-10. 「등고선 굽기」가 확장 8회 합 19s 였는데, 실측해 보니 굽기 자체는 0.8~2.0s 이고 나머지는 **피처 4,200개를 회차마다 다시 `shape()` 로 푸는 값**이었음. 푸는 결과는 격자와 무관하므로 같은 목록·같은 하한이면 그대로 쓴다(목록 객체를 함께 들고 있어 id 가 다른 목록에 재사용되지 않음, 최근 2벌만 보관). 실측 — 같은 격자 연속 4회: 2.66 · 1.90 · 1.99 · 2.03s (2회차부터 파싱 재사용), 결과 배열은 4회 모두 동일. `build_contour_descent` 전체는 옛 코드 12.8s → 7.2s 이며 band_elevation·valid·receiver·step_length·azimuth·levels 전부 `np.array_equal` 동일. 안 한 것 — 격자가 커질 때 옛 굽기를 옮겨 붙이고 **테두리만** 굽는 안은 만들어 재 봤으나 `rasterize` 호출당 고정 비용 때문에 오히려 3.2s → 10.6s 로 느렸고, 창에 걸치는 선만 넘기도록 고쳐도 3.8s 로 손해였음(창 4개 × 166단). 그래서 걷어내고 파싱 캐시만 남김. `levels` 는 피처 전체에서 나오므로 격자와 무관하다는 것도 확인함. Co-Authored-By: Claude Opus 5 (1M context) --- ...B04_PreProcess_Engine_Watershed_Descent.py | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/B04_PreProcess/B04_PreProcess_Engine_Watershed_Descent.py b/B04_PreProcess/B04_PreProcess_Engine_Watershed_Descent.py index 5f7a54e7..e854be43 100644 --- a/B04_PreProcess/B04_PreProcess_Engine_Watershed_Descent.py +++ b/B04_PreProcess/B04_PreProcess_Engine_Watershed_Descent.py @@ -72,12 +72,25 @@ class ContourDescent: levels: list[float] # 사용된 등고 표고(내림차순) -def rasterize_contours( - spec: GridSpec, - contour_features: list[dict[str, Any]], - elevation_floor_m: float | None = None, -) -> tuple[np.ndarray, list[float]]: - """등고선을 격자에 굽는다. 셀마다 그 위를 지나는 등고 라인의 표고(없으면 NaN).""" +_LINES_CACHE: list[tuple[Any, int, float | None, dict[float, list[Any]]]] = [] + + +def _lines_by_level( + contour_features: list[dict[str, Any]], elevation_floor_m: float | None +) -> dict[float, list[Any]]: + """등고선 피처를 표고별 선 묶음으로 푼다 — **격자와 무관**하므로 한 번만 푼다. + + 확장 회차마다 다시 부르는데 피처 4,200개를 매번 `shape()` 로 푸는 비용이 그대로 + 붙었다. 같은 목록·같은 하한이면 그대로 돌려준다(목록 객체를 함께 들고 있어 id 가 + 다른 목록에 재사용되지 않는다). + """ + for holder, count, floor, cached in _LINES_CACHE: + if ( + holder is contour_features + and count == len(contour_features) + and floor == elevation_floor_m + ): + return cached by_level: dict[float, list[Any]] = {} for feature in contour_features: geometry = feature.get("geometry") @@ -96,7 +109,18 @@ def rasterize_contours( if line.length < DRAINAGE_CONTOUR_MIN_LENGTH_M: continue by_level.setdefault(float(elevation), []).append(line) + _LINES_CACHE.append((contour_features, len(contour_features), elevation_floor_m, by_level)) + del _LINES_CACHE[:-2] + return by_level + +def rasterize_contours( + spec: GridSpec, + contour_features: list[dict[str, Any]], + elevation_floor_m: float | None = None, +) -> tuple[np.ndarray, list[float]]: + """등고선을 격자에 굽는다. 셀마다 그 위를 지나는 등고 라인의 표고(없으면 NaN).""" + by_level = _lines_by_level(contour_features, elevation_floor_m) burned = np.full((spec.n_rows, spec.n_cols), np.nan, dtype=np.float32) levels = sorted(by_level, reverse=True) transform = grid_transform(spec)