# run-script API 성능 최적화 기록 ## 결과 요약 | 단계 | 소요 시간 | |------|----------| | 최적화 전 | 35.12초 | | 1차 최적화 후 | ~12초 | | 2차 최적화 후 | ~8~10초 (예상) | --- ## 1차 최적화 (~35초 → ~12초) ### 문제 원인 | 위치 | 원인 | |------|------| | `Inference.py` 모듈 레벨 | `init_segmentor()` 호출 → 요청마다 GPU 모델 재로딩 (15~20초) | | `api.py` | `subprocess.run()` 으로 `Combine_module.py` 실행 → 매 요청마다 Python 인터프리터 재시작 | | `Combine_module.py` | Step1~4를 순차 subprocess 4개로 실행 | | `Georeference.py` 내부 루프 | `np.ones((3,3), np.uint8)` dilate 커널을 루프마다 재생성 | | `Oilshape.py pixel_to_geo()` | `rasterio.transform.xy()` 를 좌표 1개씩 루프로 호출 | | `Inference.py` 내부 루프 | `palette_array = np.array(model.PALETTE)` 를 이미지마다 재생성 | ### 개선 내용 **`mx15hdi/Detect/Inference.py`** - `load_model()` 함수 분리 — 모델 초기화를 서버 시작 시 1회로 분리 - `run_inference(model, file_id)` 함수화 — 사전 로드된 모델을 인자로 수신 - `palette_array` 루프 외부로 이동 (이미지마다 재생성 제거) - `cv2.cvtColor` 제거 → numpy 슬라이싱 `color_mask[:, :, ::-1].copy()` 로 대체 **`mx15hdi/Metadata/Scripts/Export_Metadata_mx15hdi.py`** - 모듈 레벨 `PaddleOCR()` 초기화 제거 → `_get_ocr_engine()` lazy 초기화로 변경 - `run_metadata_export(file_id)` 함수화 + 절대 경로(`_MX15HDI_DIR`) 기반으로 전환 - deprecated API 수정: `fillna(method='ffill')` → `ffill()` / `bfill()` **`mx15hdi/Georeference/Scripts/Create_Georeferenced_Images_nadir.py`** - `run_georeference(file_id)` 함수화 + 절대 경로 기반으로 전환 - `dilate_kernel = np.ones((3,3), np.uint8)` 루프 외부로 이동 (루프마다 재생성 제거) **`mx15hdi/Polygon/Scripts/Oilshape.py`** - `run_oilshape(file_id)` 함수화 + 절대 경로 기반으로 전환 - `pixel_to_geo()` 벡터화: 좌표 배열 일괄 처리 (`rasterio.transform.xy` 배열 입력) **`mx15hdi/Main/Combine_module.py`** - subprocess 4개 → 직접 함수 호출로 교체 - `run_pipeline(file_id, model=None)` 함수 추가 **`api.py`** - FastAPI `lifespan` 이벤트로 서버 시작 시 모델 1회 로딩 - `ThreadPoolExecutor(max_workers=4)` 추가 - `_run_mx15hdi_pipeline()` 비동기 함수: Step1(추론) + Step2(메타데이터)를 `asyncio.gather`로 병렬 실행 --- ## 2차 최적화 (~12초 → ~8~10초) ### 문제 원인 | 위치 | 원인 | |------|------| | `Inference.py` | `cv2.imread(image_path)` 로드 후 `inference_segmentor(model, image_path)` 에 경로 문자열 전달 → mmseg 내부에서 동일 이미지 재읽기 | | `Inference.py` → `Georeference.py` | blended/mask를 `Detect/result/`, `Detect/Mask_result/` 에 저장 후 georeference에서 다시 읽음 (디스크 왕복 1) | | `Georeference.py` → `Oilshape.py` | mask를 `Georeference/Mask_Tif/` 에 LZW 압축 GeoTIF로 저장 후 oilshape에서 다시 읽음 (디스크 왕복 2) | | `Georeference.py` | `scipy.ndimage.binary_dilation` 사용 (Python 구현, OpenCV 대비 느림) | | `Georeference.py` | rgb와 mask의 빈 픽셀 fill_mask를 중복 계산 | ### 개선 내용 **`mx15hdi/Detect/Inference.py`** - `inference_segmentor(model, img_bgr)` — 경로 대신 배열 직접 전달 (이중 읽기 제거) - `write_files: bool = False` 파라미터 추가 — 중간 파일 저장 선택적 처리 - `inference_cache` dict 반환: `{filename: {'blended': ndarray, 'mask': ndarray, 'ext': str}}` **`mx15hdi/Georeference/Scripts/Create_Georeferenced_Images_nadir.py`** - `inference_cache: dict = None` 파라미터 추가 — 있으면 메모리 배열 사용, 없으면 디스크 폴백 - `scipy.ndimage.binary_dilation` 제거 → `cv2.dilate` 로 통일 - rgb/mask 공통 `fill_mask` 재사용 — 중복 계산 제거 - Mask_Tif GeoTIF 디스크 저장 생략 — `georef_cache` 로 반환 - `georef_cache` dict 반환: `{filename: {'mask': ndarray, 'transform': ..., 'crs': ...}}` **`mx15hdi/Polygon/Scripts/Oilshape.py`** - `georef_cache: Optional[dict] = None` 파라미터 추가 - 있으면 메모리 배열 직접 처리, 없으면 `Mask_Tif/` 디스크 폴백 - `_process_mask_entry()` 헬퍼 함수 분리 **`api.py`** - `_run_mx15hdi_pipeline()` 캐시 체이닝: ```python inference_cache, _ = await asyncio.gather( loop.run_in_executor(_executor, run_inference, _model, file_id), loop.run_in_executor(_executor, run_metadata_export, file_id), ) georef_cache = await loop.run_in_executor( _executor, run_georeference, file_id, inference_cache ) await loop.run_in_executor(_executor, run_oilshape, file_id, georef_cache) ``` ### 디스크 I/O 흐름 변화 **최적화 전:** ``` Inference → Detect/result/ (write) Detect/Mask_result/ (write) Georeference ← Detect/result/ (read) ← 왕복 1 Detect/Mask_result/ (read) Georeference → Georeference/Mask_Tif/ (write) Oilshape ← Georeference/Mask_Tif/ (read) ← 왕복 2 ``` **최적화 후:** ``` Inference → inference_cache (메모리) Georeference ← inference_cache (메모리) ← 왕복 1 제거 Georeference → georef_cache (메모리) Oilshape ← georef_cache (메모리) ← 왕복 2 제거 Georeference → Georeference/Tif/ (write) ← /get-image/ API 전용, 유지 ``` --- ## 수정 파일 목록 | 파일 | 1차 | 2차 | |------|-----|-----| | `mx15hdi/Detect/Inference.py` | ✅ | ✅ | | `mx15hdi/Metadata/Scripts/Export_Metadata_mx15hdi.py` | ✅ | — | | `mx15hdi/Georeference/Scripts/Create_Georeferenced_Images_nadir.py` | ✅ | ✅ | | `mx15hdi/Polygon/Scripts/Oilshape.py` | ✅ | ✅ | | `mx15hdi/Main/Combine_module.py` | ✅ | — | | `api.py` | ✅ | ✅ |