Phase 1 (#11): perf instrumentation — harness/perf.py + 3 hotspot wraps
신규 모듈 — harness/perf.py (54 LOC):
- perf_block(label) 컨텍스트 매니저 — 블록 단위 wall-clock + CPU 시간을 ms 단위로 측정.
- set_perf_log(sink) — 외부 sink 등록 (예: app.log). 등록 후 [PERF] 라인이 logger
외에도 그 sink 에 라우팅됨.
- 출력 형식: [PERF] {label}: wall={NN}ms cpu={NN}ms ({CPU|I/O/Net}-bound).
- cpu/wall > 0.5 면 CPU-bound 로 분류, 그 외 I/O/Net-bound (GIL 풀린 시간 비율).
Setup — scanvas_maker.py 2곳:
- import 블록 (~line 58): from harness.perf import perf_block, set_perf_log;
ImportError 시 contextlib.contextmanager 노옵 폴백 (모듈 누락 환경 대응).
- SCanvasApp.__init__ (~line 613): set_perf_log(self.log) 등록.
Hotspot wraps — scanvas_maker.py 3곳 (PERFORMANCE_BASELINE.md 매핑):
- TIN densify Phase C (line ~4430) → H3: with perf_block("TIN densify Phase C (10m→1m)").
- 위성 타일 다운로드 (line ~5384) → H1: with perf_block("위성 타일 다운로드+병합").
- 제어맵 캡처 x3 + composite (line ~5864) → H12: with perf_block("control map capture x3 + composite").
검증:
- python -m py_compile scanvas_maker.py harness/perf.py 통과.
- AST parse OK (39 top-level statements).
- ruff Green 정식 검증은 글로벌 ruff 설치 후 (uv pip install -e ".[dev]"; ruff check).
CHANGELOG.md 에 #11 perf instrumentation 항목 추가 (2026-05-08).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
125
scanvas_maker.py
125
scanvas_maker.py
@@ -55,6 +55,16 @@ try:
|
||||
except ImportError:
|
||||
HARNESS_AVAILABLE = False
|
||||
|
||||
# Perf instrumentation (#11) — ms 단위 wall/CPU 측정. import 실패 시 no-op 폴백.
|
||||
try:
|
||||
from harness.perf import perf_block, set_perf_log
|
||||
except ImportError:
|
||||
@contextlib.contextmanager
|
||||
def perf_block(label): # type: ignore[no-redef]
|
||||
yield
|
||||
def set_perf_log(fn): # type: ignore[no-redef]
|
||||
pass
|
||||
|
||||
# 구조물 상세도면 치수 파서
|
||||
try:
|
||||
from detail_parser import DetailParser, dimensions_to_structure_params
|
||||
@@ -610,6 +620,10 @@ class SCanvasApp(ctk.CTk):
|
||||
|
||||
self.log("S-CANVAS Generative Design Engine 구동 완료.")
|
||||
|
||||
# Perf 측정 라인을 GUI 로그에도 함께 표시 (#11). harness/perf.py 폴백 import 시
|
||||
# set_perf_log는 no-op이라 실패해도 안전.
|
||||
set_perf_log(self.log)
|
||||
|
||||
def create_sidebar_button(self, text, command, row, **kwargs):
|
||||
btn = ctk.CTkButton(
|
||||
self.sidebar_frame, text=text, command=command, height=34, **kwargs)
|
||||
@@ -4427,40 +4441,41 @@ class SCanvasApp(ctk.CTk):
|
||||
from matplotlib.path import Path as _MplPath
|
||||
total_phase_c = 0
|
||||
steps_log = []
|
||||
for _step in (10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0):
|
||||
try:
|
||||
hull_c = _ConvexHull(pts[:, :2])
|
||||
except Exception:
|
||||
break
|
||||
hull_poly_xy = pts[hull_c.vertices, :2]
|
||||
hull_path_c = _MplPath(hull_poly_xy, closed=True)
|
||||
gx = np.arange(x0_abs, x1_abs + _step * 0.5, _step)
|
||||
gy = np.arange(y0_abs, y1_abs + _step * 0.5, _step)
|
||||
ggx, ggy = np.meshgrid(gx, gy)
|
||||
grid_xy_c = np.column_stack([ggx.ravel(), ggy.ravel()])
|
||||
inside_bbox = (
|
||||
(grid_xy_c[:, 0] >= x0_abs - 1e-6)
|
||||
& (grid_xy_c[:, 0] <= x1_abs + 1e-6)
|
||||
& (grid_xy_c[:, 1] >= y0_abs - 1e-6)
|
||||
& (grid_xy_c[:, 1] <= y1_abs + 1e-6)
|
||||
)
|
||||
grid_xy_c = grid_xy_c[inside_bbox]
|
||||
if len(grid_xy_c) == 0:
|
||||
continue
|
||||
inside_hull = hull_path_c.contains_points(grid_xy_c)
|
||||
outside_hull_xy = grid_xy_c[~inside_hull]
|
||||
if len(outside_hull_xy) == 0:
|
||||
continue
|
||||
# 기존 점과 너무 가까운 격자점(≤ step×0.4) 제외 — 중복 방지
|
||||
tree_ex = _cKDTreeC(pts[:, :2])
|
||||
d_ex, _ = tree_ex.query(outside_hull_xy, k=1)
|
||||
new_only_xy = outside_hull_xy[d_ex > _step * 0.4]
|
||||
if len(new_only_xy) == 0:
|
||||
continue
|
||||
new_z_c = _dem_sample_minus_offset(new_only_xy)
|
||||
pts = np.vstack([pts, np.column_stack([new_only_xy, new_z_c])])
|
||||
total_phase_c += len(new_only_xy)
|
||||
steps_log.append(f"{_step:.0f}m:{len(new_only_xy)}")
|
||||
with perf_block("TIN densify Phase C (10m→1m)"):
|
||||
for _step in (10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0):
|
||||
try:
|
||||
hull_c = _ConvexHull(pts[:, :2])
|
||||
except Exception:
|
||||
break
|
||||
hull_poly_xy = pts[hull_c.vertices, :2]
|
||||
hull_path_c = _MplPath(hull_poly_xy, closed=True)
|
||||
gx = np.arange(x0_abs, x1_abs + _step * 0.5, _step)
|
||||
gy = np.arange(y0_abs, y1_abs + _step * 0.5, _step)
|
||||
ggx, ggy = np.meshgrid(gx, gy)
|
||||
grid_xy_c = np.column_stack([ggx.ravel(), ggy.ravel()])
|
||||
inside_bbox = (
|
||||
(grid_xy_c[:, 0] >= x0_abs - 1e-6)
|
||||
& (grid_xy_c[:, 0] <= x1_abs + 1e-6)
|
||||
& (grid_xy_c[:, 1] >= y0_abs - 1e-6)
|
||||
& (grid_xy_c[:, 1] <= y1_abs + 1e-6)
|
||||
)
|
||||
grid_xy_c = grid_xy_c[inside_bbox]
|
||||
if len(grid_xy_c) == 0:
|
||||
continue
|
||||
inside_hull = hull_path_c.contains_points(grid_xy_c)
|
||||
outside_hull_xy = grid_xy_c[~inside_hull]
|
||||
if len(outside_hull_xy) == 0:
|
||||
continue
|
||||
# 기존 점과 너무 가까운 격자점(≤ step×0.4) 제외 — 중복 방지
|
||||
tree_ex = _cKDTreeC(pts[:, :2])
|
||||
d_ex, _ = tree_ex.query(outside_hull_xy, k=1)
|
||||
new_only_xy = outside_hull_xy[d_ex > _step * 0.4]
|
||||
if len(new_only_xy) == 0:
|
||||
continue
|
||||
new_z_c = _dem_sample_minus_offset(new_only_xy)
|
||||
pts = np.vstack([pts, np.column_stack([new_only_xy, new_z_c])])
|
||||
total_phase_c += len(new_only_xy)
|
||||
steps_log.append(f"{_step:.0f}m:{len(new_only_xy)}")
|
||||
if total_phase_c > 0:
|
||||
self.log(
|
||||
f" [Phase C] hull 바깥 × bbox 내부 점진 densify: "
|
||||
@@ -5380,7 +5395,8 @@ class SCanvasApp(ctk.CTk):
|
||||
if not vk:
|
||||
raise ValueError("Vworld 타일 사용 시 API Key가 필요합니다. 사이드바에 입력해주세요.")
|
||||
tile_url_template = tile_url_template.replace("{vworld_key}", vk)
|
||||
satellite_img = self._download_xyz_tiles(tile_url_template, min_lat, min_lon, max_lat, max_lon)
|
||||
with perf_block("위성 타일 다운로드+병합"):
|
||||
satellite_img = self._download_xyz_tiles(tile_url_template, min_lat, min_lon, max_lat, max_lon)
|
||||
|
||||
img_path = "satellite_temp.png"
|
||||
satellite_img.save(img_path)
|
||||
@@ -5861,28 +5877,29 @@ class SCanvasApp(ctk.CTk):
|
||||
ar_label = f"비율 {ar[0]}:{ar[1]}" if ar else f"뷰어 창 {self._saved_window_size or '미저장'}"
|
||||
self.log(f" 캡처 해상도: {out_w}x{out_h} ({ar_label} 기반)")
|
||||
|
||||
# 1. 위성 텍스처 3D 캡처
|
||||
self.capture_image = self._capture_from_camera(out_w, out_h, textured=True)
|
||||
self.capture_image.save("capture_textured.png")
|
||||
self.log(f" 캡처 완료: {self.capture_image.size}")
|
||||
with perf_block("control map capture x3 + composite"):
|
||||
# 1. 위성 텍스처 3D 캡처
|
||||
self.capture_image = self._capture_from_camera(out_w, out_h, textured=True)
|
||||
self.capture_image.save("capture_textured.png")
|
||||
self.log(f" 캡처 완료: {self.capture_image.size}")
|
||||
|
||||
# 2. Depth Map
|
||||
self.log(" Depth Map 추출 중...")
|
||||
self.depth_map = self._capture_depth_from_camera(out_w, out_h)
|
||||
self.depth_map.save("depth_map.png")
|
||||
self.log(" Depth Map 완료.")
|
||||
# 2. Depth Map
|
||||
self.log(" Depth Map 추출 중...")
|
||||
self.depth_map = self._capture_depth_from_camera(out_w, out_h)
|
||||
self.depth_map.save("depth_map.png")
|
||||
self.log(" Depth Map 완료.")
|
||||
|
||||
# 3. Lineart Map
|
||||
self.log(" Lineart Map 추출 중...")
|
||||
self.lineart_map = self._capture_lineart_from_camera(out_w, out_h)
|
||||
self.lineart_map.save("lineart_map.png")
|
||||
self.log(" Lineart Map 완료.")
|
||||
# 3. Lineart Map
|
||||
self.log(" Lineart Map 추출 중...")
|
||||
self.lineart_map = self._capture_lineart_from_camera(out_w, out_h)
|
||||
self.lineart_map.save("lineart_map.png")
|
||||
self.log(" Lineart Map 완료.")
|
||||
|
||||
# 4. 가이드 이미지 합성
|
||||
self.guide_image = self._compose_guide_image(
|
||||
self.capture_image, self.depth_map, self.lineart_map
|
||||
)
|
||||
self.guide_image.save("guide_composite.png")
|
||||
# 4. 가이드 이미지 합성
|
||||
self.guide_image = self._compose_guide_image(
|
||||
self.capture_image, self.depth_map, self.lineart_map
|
||||
)
|
||||
self.guide_image.save("guide_composite.png")
|
||||
|
||||
self.set_status("제어맵 추출 완료", "#2ECC71")
|
||||
self.btn_step4.configure(fg_color=["#3a7ebf", "#1f538d"])
|
||||
|
||||
Reference in New Issue
Block a user