7.4 KiB
P&ID 그래프 병목 진단 및 수정 방안
작성일: 2026-05-04
대상 파일:futurePlan/End-to-End P&ID Graph Pipeline/No-10_Plant_PID.dxf
상태: 진단 완료, 수정 계획 수립
1. DXF 파일 특성
| 항목 | 값 |
|---|---|
| 파일 크기 | 1,174,227 라인 (약 1.1MB) |
| 총 엔티티 | 28,819개 |
| LINE | 20,868개 (72.4%) |
| TEXT | 3,562개 (12.4%) |
| ARC | 1,324개 (4.6%) |
| CIRCLE | 1,275개 (4.4%) |
| LWPOLYLINE | 865개 (3.0%) |
| MTEXT | 363개 (1.3%) |
| 기타 | 102개 (0.4%) |
2. 확인된 병목 지점
🔴 병목 1: Phase 2 - build_graph() 내 배관-설비 연결 로직 (HIGH)
위치: mcp-server/pipeline/topology.py:81-129
문제: LINE/LWPOLYLINE(약 21,733개)과 설비(CIRCLE/ARC/기타, 약 3,272개)를 전체 조합으로 비교
# topology.py:82-99
lines = [n for n, d in self.G.nodes(data=True) if d['type'] in ['LINE', 'LWPOLYLINE']]
for line_id in lines: # 21,733개
...
for eq_id in equipments: # 3,272개
eq_bbox = self.G.nodes[eq_id]['bbox']
if line_geom.intersects(eq_bbox): # shapely 교차 계산 (고비용)
connected_nodes.append(eq_id)
elif line_geom.distance(eq_bbox) < self.config['dist_threshold']: # shapely 거리 계산 (고비용)
connected_nodes.append(eq_id)
연산량: 21,733 × 3,272 = 약 7,110만 회의 shapely 기하학 연산
실제 성능: 10분 이상 (타임아웃 발생)
🟠 병목 2: Phase 2 - _find_nearest_equipment() 태그-설비 매핑 (MED)
위치: mcp-server/pipeline/topology.py:131-163
문제: TEXT/MTEXT(약 3,925개)와 설비(약 3,272개)를 전체 비교
# topology.py:143-148
for eq_id in equipment_ids: # 3,272개
eq_bbox = self.G.nodes[eq_id]['bbox']
dist = tag_bbox.distance(eq_bbox) # shapely 거리 계산
if dist > self.config['tag_threshold']:
continue
연산량: 3,925 × 3,272 = 약 1,280만 회의 거리 계산
실제 성능: 수초 ~ 수십초 (설비 수에 따라 변동)
🟡 병목 3: Phase 1 - DXF 엔티티 순회 추출 (LOW)
위치: mcp-server/pipeline/extractor.py:124-162
문제: 28,819개 엔티티를 하나씩 순회하며 BBox 계산
# extractor.py:124-162
for entity in self.msp: # 28,819개
bbox_obj = self.get_bbox(entity) # 각 엔티티당 BBox 계산
...
실제 성능: ~1.4초 (허용 가능)
3. 근본 원인
공간 인덱스(R-tree, Quadtree) 미사용으로 모든 노드 쌍을 O(n²)으로 비교함.
shapely의 intersects(), distance()는 정확한 기하학 연산이지만 계산 비용이 큼.
불필요하게 먼 객체 쌍에도 이 연산을 수행하는 것이 핵심 문제.
4. 수정 계획
전략: 3단계 점진적 개선
| 단계 | 내용 | 예상 효과 | 작업량 |
|---|---|---|---|
| Step 1 | BBox 빠른 필터링 추가 | 90% 이상 연산 축소 | 소 |
| Step 2 | 그리드 기반 공간 인덱스 | O(n²) → O(n log n) | 중 |
| Step 3 | pid_worker.py 통합 및 테스트 | E2E 검증 | 소 |
5. 상세 TODO List
각 단계는 독립적으로 커밋 가능. 중간에 끊겨도 다음 작업자가 이 문서로 이어갈 수 있음.
Step 1: BBox 빠른 필터링 추가 (우선순위: HIGH)
-
1-1.
topology.py에_bbox_quick_filter()함수 추가- shapely 없이
min_x/max_x/min_y/max_y로 간단한 거리 계산 - 임계값보다 먼 객체는 즉시 제외
- 참고:
mcp-server/pipeline/extractor.py:176-183의is_near()함수가 이미 shapely box 사용 중 → 이 함수도 함께 수정 필요
- shapely 없이
-
1-2.
build_graph()내 배관-설비 연결 루프 수정 (topology.py:81-129)- 기존:
for eq_id in equipments:→ 전체 순회 - 수정:
_bbox_quick_filter()로 후보 집합 축소 후 순회 - 예상: 7,110만 회 → 약 100만 회로 축소
- 기존:
-
1-3.
_find_nearest_equipment()수정 (topology.py:131-163)- 동일하게 BBox 빠른 필터링 적용
- 예상: 1,280만 회 → 약 50만 회로 축소
-
1-4. 성능 테스트 실행
test_pid_bottleneck_analysis.py로 Phase 2 시간 측정- 목표: 10분 → 30초 이내
-
1-5. 커밋:
fix(#bottleneck): topology.py BBox 빠른 필터링 추가
Step 2: 그리드 기반 공간 인덱스 (우선순위: MED)
-
2-1.
topology.py에SpatialGrid클래스 추가- 그리드 셀 크기 =
dist_threshold - 각 엔티티를 중심 좌표로 그리드 셀에 할당
- 인접 셀(3×3)만 검색 대상
- 그리드 셀 크기 =
-
2-2.
build_graph()에서SpatialGrid사용- 배관-설비 연결 시 그리드 기반 후보 검색
- 예상: 100만 회 → 약 10만 회로 추가 축소
-
2-3.
_find_nearest_equipment()에서SpatialGrid사용- 태그-설비 매핑 시 그리드 기반 후보 검색
-
2-4. 성능 테스트
- 목표: 30초 → 2초 이내
-
2-5. 커밋:
perf(#bottleneck): topology.py 그리드 기반 공간 인덱스 도입
Step 3: pid_worker.py 통합 및 E2E 테스트 (우선순위: LOW)
-
3-1.
pid_worker.py의build_pid_graph_parallel도구 확인mcp-server/worker/pid_worker.py:201-507읽기- 최적화된
topology.py가 정상적으로 호출되는지 확인
-
3-2. MCP 서버 재시작 후 E2E 테스트
parse_pid_drawing도구로 No-10_Plant_PID.dxf 파싱- 전체 파이프라인 완료 시간 측정
-
3-3. 결과 검증
- 생성된 그래프 JSON 확인
- 노드/엣지 수 비교 (기존 결과와 일치하는지)
-
3-4. 커밋:
test(#bottleneck): E2E 테스트 및 성능 검증
6. 참고: 관련 파일 목록
| 파일 | 역할 | 수정 대상 |
|---|---|---|
mcp-server/pipeline/topology.py |
위상 빌더 | 주요 수정 대상 |
mcp-server/pipeline/extractor.py |
기하학적 추출 | is_near() 함수 수정 |
mcp-server/pipeline/mapper.py |
지능형 매핑 | 변경 없음 |
mcp-server/pipeline/analyzer.py |
영향도 분석 | 변경 없음 |
mcp-server/worker/pid_worker.py |
P&ID 워커 | Step 3 통합 테스트 |
test_pid_bottleneck_analysis.py |
성능 테스트 | Step 1-2 검증용 |
7. 진행 상태
| 단계 | 상태 | 비고 |
|---|---|---|
| 진단 | ✅ 완료 | 2026-05-04 |
| Step 1: BBox 빠른 필터링 | ✅ 완료 | SpatialGrid로 통합 |
| Step 2: 그리드 기반 공간 인덱스 | ✅ 완료 | SpatialGrid 클래스 추가 |
| Step 3: E2E 테스트 | ✅ 완료 | 성능 검증 완료 |
| 추가: build_graph() 중복 제거 | ✅ 완료 | pid_worker.py 수정 |
8. 복귀 가이드
작업을 끊었다가 다시 시작할 때:
- 이 문서의 "진행 상태" 테이블 확인
- 가장 최근 완료된 Step 다음 TODO부터 시작
- 각 Step의 마지막 항목이 "커밋"이므로, git log로 실제 진행 확인
- 성능 테스트는
test_pid_bottleneck_analysis.py실행