py-alaska 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_alaska/SmBlock.py +263 -0
- py_alaska/__init__.py +63 -0
- py_alaska/div_logo.png +0 -0
- py_alaska/gconfig.py +1241 -0
- py_alaska/imi_camera.py +391 -0
- py_alaska/tab_camera.py +730 -0
- py_alaska/task_manager.py +661 -0
- py_alaska/task_monitor.py +1533 -0
- py_alaska/task_performance.py +550 -0
- py_alaska/task_signal.py +238 -0
- py_alaska-0.1.0.dist-info/METADATA +263 -0
- py_alaska-0.1.0.dist-info/RECORD +15 -0
- py_alaska-0.1.0.dist-info/WHEEL +5 -0
- py_alaska-0.1.0.dist-info/licenses/LICENSE +21 -0
- py_alaska-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
"""
|
|
2
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
║ ALASK v2.0 Project ║
|
|
4
|
+
║ Task Performance - RMI 성능 측정 및 통계 관리 모듈 ║
|
|
5
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
6
|
+
║ Version : 2.0.0 ║
|
|
7
|
+
║ Date : 2026-02-01 ║
|
|
8
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
9
|
+
║ Classes: ║
|
|
10
|
+
║ - MethodPerformance : 메서드별 성능 통계 (IPC/FUNC min/avg/max) ║
|
|
11
|
+
║ - TaskPerformance : Task별 성능 통계 집계 ║
|
|
12
|
+
║ - SystemPerformance : 시스템 전체 성능 통계 ║
|
|
13
|
+
║ - TimingRecorder : TaskWorker용 로컬 타이밍 기록 및 배치 플러시 ║
|
|
14
|
+
║ - PerformanceCollector: TaskManager용 시스템 통계 수집기 ║
|
|
15
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import Dict, List, Any, Optional, Tuple, TYPE_CHECKING, ClassVar
|
|
21
|
+
import time
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from .task_manager import TaskInfo
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
28
|
+
# 데이터 클래스 (PERF-003, PERF-002, PERF-001)
|
|
29
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class MethodPerformance:
|
|
33
|
+
"""PERF-003: 메서드별 성능 통계
|
|
34
|
+
|
|
35
|
+
각 RMI 메서드의 호출 횟수와 IPC/FUNC 시간 통계를 관리합니다.
|
|
36
|
+
최근 WINDOW_SIZE(10)개의 측정값을 기반으로 min/avg/max를 계산합니다.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
name: 메서드 이름
|
|
40
|
+
call_count: 호출 횟수
|
|
41
|
+
ipc_window: IPC 시간 윈도우 (최근 10개, ms)
|
|
42
|
+
func_window: FUNC 시간 윈도우 (최근 10개, ms)
|
|
43
|
+
"""
|
|
44
|
+
name: str
|
|
45
|
+
call_count: int = 0
|
|
46
|
+
ipc_window: List[float] = field(default_factory=list)
|
|
47
|
+
func_window: List[float] = field(default_factory=list)
|
|
48
|
+
|
|
49
|
+
WINDOW_SIZE: ClassVar[int] = 10
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def ipc_stats(self) -> Dict[str, float]:
|
|
53
|
+
"""IPC 시간 통계 (min/avg/max in ms)"""
|
|
54
|
+
if not self.ipc_window:
|
|
55
|
+
return {'min': 0.0, 'avg': 0.0, 'max': 0.0}
|
|
56
|
+
return {
|
|
57
|
+
'min': round(min(self.ipc_window), 2),
|
|
58
|
+
'avg': round(sum(self.ipc_window) / len(self.ipc_window), 2),
|
|
59
|
+
'max': round(max(self.ipc_window), 2)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def func_stats(self) -> Dict[str, float]:
|
|
64
|
+
"""FUNC 시간 통계 (min/avg/max in ms)"""
|
|
65
|
+
if not self.func_window:
|
|
66
|
+
return {'min': 0.0, 'avg': 0.0, 'max': 0.0}
|
|
67
|
+
return {
|
|
68
|
+
'min': round(min(self.func_window), 2),
|
|
69
|
+
'avg': round(sum(self.func_window) / len(self.func_window), 2),
|
|
70
|
+
'max': round(max(self.func_window), 2)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def total_time_avg(self) -> float:
|
|
75
|
+
"""평균 총 소요 시간 (IPC + FUNC)"""
|
|
76
|
+
return round(self.ipc_stats['avg'] + self.func_stats['avg'], 2)
|
|
77
|
+
|
|
78
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
79
|
+
"""JSON 직렬화용 딕셔너리 변환"""
|
|
80
|
+
return {
|
|
81
|
+
'name': self.name,
|
|
82
|
+
'count': self.call_count,
|
|
83
|
+
'ipc': self.ipc_stats,
|
|
84
|
+
'func': self.func_stats,
|
|
85
|
+
'total_avg': self.total_time_avg,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class TaskPerformance:
|
|
91
|
+
"""PERF-002: Task별 성능 통계
|
|
92
|
+
|
|
93
|
+
개별 Task(프로세스/스레드)의 성능 통계를 집계합니다.
|
|
94
|
+
|
|
95
|
+
Attributes:
|
|
96
|
+
task_id: Task 식별자
|
|
97
|
+
task_class: Task 클래스명
|
|
98
|
+
mode: 실행 모드 ('thread' | 'process')
|
|
99
|
+
alive: Task 활성 여부
|
|
100
|
+
methods: 메서드별 성능 데이터
|
|
101
|
+
"""
|
|
102
|
+
task_id: str
|
|
103
|
+
task_class: str = ""
|
|
104
|
+
mode: str = "thread"
|
|
105
|
+
alive: bool = False
|
|
106
|
+
methods: Dict[str, MethodPerformance] = field(default_factory=dict)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def total_calls(self) -> int:
|
|
110
|
+
"""전체 메서드 호출 수"""
|
|
111
|
+
return sum(m.call_count for m in self.methods.values())
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def method_count(self) -> int:
|
|
115
|
+
"""등록된 메서드 수"""
|
|
116
|
+
return len(self.methods)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def avg_ipc_time(self) -> float:
|
|
120
|
+
"""평균 IPC 시간 (모든 메서드의 윈도우 평균)"""
|
|
121
|
+
all_ipc = []
|
|
122
|
+
for m in self.methods.values():
|
|
123
|
+
all_ipc.extend(m.ipc_window)
|
|
124
|
+
return round(sum(all_ipc) / len(all_ipc), 2) if all_ipc else 0.0
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def avg_func_time(self) -> float:
|
|
128
|
+
"""평균 FUNC 시간 (모든 메서드의 윈도우 평균)"""
|
|
129
|
+
all_func = []
|
|
130
|
+
for m in self.methods.values():
|
|
131
|
+
all_func.extend(m.func_window)
|
|
132
|
+
return round(sum(all_func) / len(all_func), 2) if all_func else 0.0
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def avg_total_time(self) -> float:
|
|
136
|
+
"""평균 총 소요 시간 (IPC + FUNC)"""
|
|
137
|
+
return round(self.avg_ipc_time + self.avg_func_time, 2)
|
|
138
|
+
|
|
139
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
140
|
+
"""JSON 직렬화용 딕셔너리 변환"""
|
|
141
|
+
return {
|
|
142
|
+
'task_id': self.task_id,
|
|
143
|
+
'task_class': self.task_class,
|
|
144
|
+
'mode': self.mode,
|
|
145
|
+
'alive': self.alive,
|
|
146
|
+
'total_calls': self.total_calls,
|
|
147
|
+
'method_count': self.method_count,
|
|
148
|
+
'avg_ipc_time': self.avg_ipc_time,
|
|
149
|
+
'avg_func_time': self.avg_func_time,
|
|
150
|
+
'avg_total_time': self.avg_total_time,
|
|
151
|
+
'methods': {k: v.to_dict() for k, v in self.methods.items()}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@dataclass
|
|
156
|
+
class SystemPerformance:
|
|
157
|
+
"""PERF-001: 시스템 전체 성능 통계
|
|
158
|
+
|
|
159
|
+
모든 Task의 성능 데이터를 집계하여 시스템 수준 통계를 제공합니다.
|
|
160
|
+
|
|
161
|
+
Attributes:
|
|
162
|
+
tasks: Task별 성능 데이터
|
|
163
|
+
measurement_enabled: 측정 활성화 여부
|
|
164
|
+
start_time: 측정 시작 시간 (Unix timestamp)
|
|
165
|
+
"""
|
|
166
|
+
tasks: Dict[str, TaskPerformance] = field(default_factory=dict)
|
|
167
|
+
measurement_enabled: bool = False
|
|
168
|
+
start_time: float = field(default_factory=time.time)
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def total_calls(self) -> int:
|
|
172
|
+
"""시스템 전체 RMI 호출 수"""
|
|
173
|
+
return sum(t.total_calls for t in self.tasks.values())
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def total_tasks(self) -> int:
|
|
177
|
+
"""등록된 Task 수"""
|
|
178
|
+
return len(self.tasks)
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def active_tasks(self) -> int:
|
|
182
|
+
"""활성 Task 수 (alive=True)"""
|
|
183
|
+
return sum(1 for t in self.tasks.values() if t.alive)
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def total_methods(self) -> int:
|
|
187
|
+
"""전체 메서드 수"""
|
|
188
|
+
return sum(t.method_count for t in self.tasks.values())
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def avg_ipc_time(self) -> float:
|
|
192
|
+
"""시스템 평균 IPC 시간"""
|
|
193
|
+
all_ipc = []
|
|
194
|
+
for t in self.tasks.values():
|
|
195
|
+
for m in t.methods.values():
|
|
196
|
+
all_ipc.extend(m.ipc_window)
|
|
197
|
+
return round(sum(all_ipc) / len(all_ipc), 2) if all_ipc else 0.0
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def avg_func_time(self) -> float:
|
|
201
|
+
"""시스템 평균 FUNC 시간"""
|
|
202
|
+
all_func = []
|
|
203
|
+
for t in self.tasks.values():
|
|
204
|
+
for m in t.methods.values():
|
|
205
|
+
all_func.extend(m.func_window)
|
|
206
|
+
return round(sum(all_func) / len(all_func), 2) if all_func else 0.0
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def avg_total_time(self) -> float:
|
|
210
|
+
"""시스템 평균 총 소요 시간"""
|
|
211
|
+
return round(self.avg_ipc_time + self.avg_func_time, 2)
|
|
212
|
+
|
|
213
|
+
@property
|
|
214
|
+
def uptime_seconds(self) -> int:
|
|
215
|
+
"""측정 시작 후 경과 시간 (초)"""
|
|
216
|
+
return int(time.time() - self.start_time)
|
|
217
|
+
|
|
218
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
219
|
+
"""JSON 직렬화용 딕셔너리 변환"""
|
|
220
|
+
return {
|
|
221
|
+
'total_calls': self.total_calls,
|
|
222
|
+
'total_tasks': self.total_tasks,
|
|
223
|
+
'active_tasks': self.active_tasks,
|
|
224
|
+
'total_methods': self.total_methods,
|
|
225
|
+
'avg_ipc_time': self.avg_ipc_time,
|
|
226
|
+
'avg_func_time': self.avg_func_time,
|
|
227
|
+
'avg_total_time': self.avg_total_time,
|
|
228
|
+
'uptime_seconds': self.uptime_seconds,
|
|
229
|
+
'measurement_enabled': self.measurement_enabled,
|
|
230
|
+
'tasks': {k: v.to_dict() for k, v in self.tasks.items()}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
235
|
+
# TimingRecorder (PERF-003, PERF-007: 기존 로직 이관)
|
|
236
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
237
|
+
|
|
238
|
+
class TimingRecorder:
|
|
239
|
+
"""TaskWorker에서 사용하는 로컬 타이밍 기록기
|
|
240
|
+
|
|
241
|
+
기존 TaskWorker의 _local_methods, _local_timing,
|
|
242
|
+
_record_timing, _flush_* 메서드를 캡슐화합니다.
|
|
243
|
+
|
|
244
|
+
로컬 캐시를 사용하여 IPC 오버헤드를 줄이고,
|
|
245
|
+
주기적으로 공유 메모리로 배치 플러시합니다.
|
|
246
|
+
|
|
247
|
+
Thread-safe: 단일 워커 내에서만 사용 (IPC 없음)
|
|
248
|
+
|
|
249
|
+
Attributes:
|
|
250
|
+
WINDOW_SIZE: 타이밍 윈도우 크기 (기본 10)
|
|
251
|
+
FLUSH_INTERVAL: 자동 플러시 간격 (기본 50회)
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
WINDOW_SIZE: int = 10
|
|
255
|
+
FLUSH_INTERVAL: int = 50
|
|
256
|
+
|
|
257
|
+
__slots__ = ('_local_methods', '_local_timing', '_flush_count',
|
|
258
|
+
'_rmi_methods', '_rmi_timing')
|
|
259
|
+
|
|
260
|
+
def __init__(self, rmi_methods, rmi_timing):
|
|
261
|
+
"""
|
|
262
|
+
Args:
|
|
263
|
+
rmi_methods: Manager.dict() - 공유 메서드 카운터
|
|
264
|
+
rmi_timing: Manager.dict() - 공유 타이밍 데이터
|
|
265
|
+
"""
|
|
266
|
+
self._local_methods: Dict[str, int] = {}
|
|
267
|
+
self._local_timing: Dict[str, Tuple[List[float], List[float]]] = {}
|
|
268
|
+
self._flush_count: int = 0
|
|
269
|
+
self._rmi_methods = rmi_methods
|
|
270
|
+
self._rmi_timing = rmi_timing
|
|
271
|
+
|
|
272
|
+
def record(self, method: str, ipc_ms: float, func_ms: float) -> None:
|
|
273
|
+
"""타이밍 데이터 기록 (로컬 캐시)
|
|
274
|
+
|
|
275
|
+
기존 TaskWorker._record_timing() 이관
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
method: 메서드 이름
|
|
279
|
+
ipc_ms: IPC 소요 시간 (ms)
|
|
280
|
+
func_ms: FUNC 소요 시간 (ms)
|
|
281
|
+
"""
|
|
282
|
+
# 메서드 호출 수 증가
|
|
283
|
+
self._local_methods[method] = self._local_methods.get(method, 0) + 1
|
|
284
|
+
|
|
285
|
+
# 타이밍 데이터 기록
|
|
286
|
+
if method not in self._local_timing:
|
|
287
|
+
self._local_timing[method] = ([], [])
|
|
288
|
+
ipc_list, func_list = self._local_timing[method]
|
|
289
|
+
ipc_list.append(ipc_ms)
|
|
290
|
+
func_list.append(func_ms)
|
|
291
|
+
|
|
292
|
+
# 로컬 캐시 크기 제한
|
|
293
|
+
if len(ipc_list) > self.WINDOW_SIZE:
|
|
294
|
+
self._local_timing[method] = (
|
|
295
|
+
ipc_list[-self.WINDOW_SIZE:],
|
|
296
|
+
func_list[-self.WINDOW_SIZE:]
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# 주기적 플러시
|
|
300
|
+
self._flush_count += 1
|
|
301
|
+
if self._flush_count >= self.FLUSH_INTERVAL:
|
|
302
|
+
self.flush()
|
|
303
|
+
self._flush_count = 0
|
|
304
|
+
|
|
305
|
+
def record_count_only(self, method: str) -> None:
|
|
306
|
+
"""호출 횟수만 기록 (타이밍 없음)
|
|
307
|
+
|
|
308
|
+
t0가 없는 경우 (one-way 호출 등)에 사용
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
method: 메서드 이름
|
|
312
|
+
"""
|
|
313
|
+
self._local_methods[method] = self._local_methods.get(method, 0) + 1
|
|
314
|
+
self._flush_count += 1
|
|
315
|
+
if self._flush_count >= self.FLUSH_INTERVAL:
|
|
316
|
+
self.flush()
|
|
317
|
+
self._flush_count = 0
|
|
318
|
+
|
|
319
|
+
def flush(self) -> None:
|
|
320
|
+
"""로컬 데이터를 공유 메모리로 플러시
|
|
321
|
+
|
|
322
|
+
기존 TaskWorker._flush_method_counts(), _flush_timing() 통합
|
|
323
|
+
"""
|
|
324
|
+
# 메서드 카운터 플러시
|
|
325
|
+
if self._local_methods:
|
|
326
|
+
for m, cnt in self._local_methods.items():
|
|
327
|
+
self._rmi_methods[m] = self._rmi_methods.get(m, 0) + cnt
|
|
328
|
+
self._local_methods.clear()
|
|
329
|
+
|
|
330
|
+
# 타이밍 데이터 플러시
|
|
331
|
+
if self._local_timing:
|
|
332
|
+
for m, (ipc_list, func_list) in self._local_timing.items():
|
|
333
|
+
existing = self._rmi_timing.get(m)
|
|
334
|
+
if existing:
|
|
335
|
+
ex_ipc, ex_func = existing
|
|
336
|
+
merged_ipc = (list(ex_ipc) + ipc_list)[-self.WINDOW_SIZE:]
|
|
337
|
+
merged_func = (list(ex_func) + func_list)[-self.WINDOW_SIZE:]
|
|
338
|
+
self._rmi_timing[m] = (merged_ipc, merged_func)
|
|
339
|
+
else:
|
|
340
|
+
self._rmi_timing[m] = (
|
|
341
|
+
ipc_list[-self.WINDOW_SIZE:],
|
|
342
|
+
func_list[-self.WINDOW_SIZE:]
|
|
343
|
+
)
|
|
344
|
+
self._local_timing.clear()
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
348
|
+
# PerformanceCollector (PERF-001, PERF-002, PERF-006)
|
|
349
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
350
|
+
|
|
351
|
+
class PerformanceCollector:
|
|
352
|
+
"""시스템 성능 데이터 수집기
|
|
353
|
+
|
|
354
|
+
TaskManager에서 생성하여 모든 Task의 성능 데이터를 집계합니다.
|
|
355
|
+
TaskMonitor가 이 객체를 통해 API 응답을 생성합니다.
|
|
356
|
+
|
|
357
|
+
3계층 성능 데이터 접근:
|
|
358
|
+
- 시스템 레벨: get_system_performance()
|
|
359
|
+
- Task 레벨: get_task_performance(task_id)
|
|
360
|
+
- 메서드 레벨: get_method_performance(task_id, method)
|
|
361
|
+
|
|
362
|
+
Process-safe: Manager 객체 기반 공유 데이터 사용
|
|
363
|
+
|
|
364
|
+
Attributes:
|
|
365
|
+
_tasks: TaskManager._tasks 참조
|
|
366
|
+
_workers: TaskManager._workers 참조 (alive 상태 확인용)
|
|
367
|
+
_measurement: 측정 ON/OFF 공유 변수
|
|
368
|
+
_start_time: 수집기 시작 시간
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
__slots__ = ('_tasks', '_workers', '_measurement', '_start_time')
|
|
372
|
+
|
|
373
|
+
def __init__(self, tasks: Dict[str, 'TaskInfo'], workers: dict, measurement):
|
|
374
|
+
"""
|
|
375
|
+
Args:
|
|
376
|
+
tasks: TaskManager._tasks 참조
|
|
377
|
+
workers: TaskManager._workers 참조
|
|
378
|
+
measurement: Manager.Value('b', bool) - 측정 ON/OFF
|
|
379
|
+
"""
|
|
380
|
+
self._tasks = tasks
|
|
381
|
+
self._workers = workers
|
|
382
|
+
self._measurement = measurement
|
|
383
|
+
self._start_time = time.time()
|
|
384
|
+
|
|
385
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
386
|
+
# PERF-001: 시스템 레벨 API
|
|
387
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
388
|
+
|
|
389
|
+
def get_system_performance(self) -> SystemPerformance:
|
|
390
|
+
"""시스템 전체 성능 통계 반환
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
SystemPerformance 객체
|
|
394
|
+
"""
|
|
395
|
+
sys_perf = SystemPerformance(
|
|
396
|
+
measurement_enabled=self._measurement.value if self._measurement else False,
|
|
397
|
+
start_time=self._start_time
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
for tid in self._tasks.keys():
|
|
401
|
+
task_perf = self.get_task_performance(tid)
|
|
402
|
+
if task_perf:
|
|
403
|
+
sys_perf.tasks[tid] = task_perf
|
|
404
|
+
|
|
405
|
+
return sys_perf
|
|
406
|
+
|
|
407
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
408
|
+
# PERF-002: Task 레벨 API
|
|
409
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
410
|
+
|
|
411
|
+
def get_task_performance(self, task_id: str) -> Optional[TaskPerformance]:
|
|
412
|
+
"""특정 Task의 성능 통계 반환
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
task_id: Task 식별자
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
TaskPerformance 객체 또는 None
|
|
419
|
+
"""
|
|
420
|
+
ti = self._tasks.get(task_id)
|
|
421
|
+
if not ti:
|
|
422
|
+
return None
|
|
423
|
+
|
|
424
|
+
# alive 상태 확인
|
|
425
|
+
worker = self._workers.get(task_id) if self._workers else None
|
|
426
|
+
alive = worker.is_alive() if worker else False
|
|
427
|
+
|
|
428
|
+
task_perf = TaskPerformance(
|
|
429
|
+
task_id=task_id,
|
|
430
|
+
task_class=ti.task_class_name,
|
|
431
|
+
mode=ti.mode,
|
|
432
|
+
alive=alive
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
# 메서드별 데이터 수집
|
|
436
|
+
methods = dict(ti.rmi_methods) if ti.rmi_methods else {}
|
|
437
|
+
timing = dict(ti.rmi_timing) if ti.rmi_timing else {}
|
|
438
|
+
|
|
439
|
+
for method_name, count in methods.items():
|
|
440
|
+
method_perf = MethodPerformance(name=method_name, call_count=count)
|
|
441
|
+
t = timing.get(method_name)
|
|
442
|
+
if t:
|
|
443
|
+
ipc_list, func_list = t
|
|
444
|
+
method_perf.ipc_window = list(ipc_list) if ipc_list else []
|
|
445
|
+
method_perf.func_window = list(func_list) if func_list else []
|
|
446
|
+
task_perf.methods[method_name] = method_perf
|
|
447
|
+
|
|
448
|
+
return task_perf
|
|
449
|
+
|
|
450
|
+
def get_all_tasks_performance(self) -> List[TaskPerformance]:
|
|
451
|
+
"""모든 Task의 성능 통계 반환
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
TaskPerformance 리스트
|
|
455
|
+
"""
|
|
456
|
+
result = []
|
|
457
|
+
for tid in self._tasks.keys():
|
|
458
|
+
perf = self.get_task_performance(tid)
|
|
459
|
+
if perf:
|
|
460
|
+
result.append(perf)
|
|
461
|
+
return result
|
|
462
|
+
|
|
463
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
464
|
+
# PERF-003: 메서드 레벨 API
|
|
465
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
466
|
+
|
|
467
|
+
def get_method_performance(self, task_id: str, method_name: str) -> Optional[MethodPerformance]:
|
|
468
|
+
"""특정 메서드의 성능 통계 반환
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
task_id: Task 식별자
|
|
472
|
+
method_name: 메서드명
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
MethodPerformance 객체 또는 None
|
|
476
|
+
"""
|
|
477
|
+
task_perf = self.get_task_performance(task_id)
|
|
478
|
+
if not task_perf:
|
|
479
|
+
return None
|
|
480
|
+
return task_perf.methods.get(method_name)
|
|
481
|
+
|
|
482
|
+
def get_task_methods_performance(self, task_id: str) -> List[MethodPerformance]:
|
|
483
|
+
"""특정 Task의 모든 메서드 성능 통계 반환
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
task_id: Task 식별자
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
MethodPerformance 리스트 (호출 수 내림차순 정렬)
|
|
490
|
+
"""
|
|
491
|
+
task_perf = self.get_task_performance(task_id)
|
|
492
|
+
if not task_perf:
|
|
493
|
+
return []
|
|
494
|
+
# 호출 수 기준 내림차순 정렬
|
|
495
|
+
return sorted(task_perf.methods.values(), key=lambda m: m.call_count, reverse=True)
|
|
496
|
+
|
|
497
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
498
|
+
# PERF-009, PERF-010: 제어 API
|
|
499
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
500
|
+
|
|
501
|
+
def set_measurement_enabled(self, enabled: bool) -> None:
|
|
502
|
+
"""측정 ON/OFF 설정
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
enabled: True=측정 활성화, False=비활성화
|
|
506
|
+
"""
|
|
507
|
+
if self._measurement:
|
|
508
|
+
self._measurement.value = enabled
|
|
509
|
+
|
|
510
|
+
def is_measurement_enabled(self) -> bool:
|
|
511
|
+
"""측정 활성화 여부 반환"""
|
|
512
|
+
return self._measurement.value if self._measurement else False
|
|
513
|
+
|
|
514
|
+
def toggle_measurement(self) -> bool:
|
|
515
|
+
"""측정 ON/OFF 토글
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
토글 후 상태
|
|
519
|
+
"""
|
|
520
|
+
if self._measurement:
|
|
521
|
+
self._measurement.value = not self._measurement.value
|
|
522
|
+
return self._measurement.value
|
|
523
|
+
return False
|
|
524
|
+
|
|
525
|
+
def clear_all(self) -> None:
|
|
526
|
+
"""모든 성능 데이터 초기화"""
|
|
527
|
+
for ti in self._tasks.values():
|
|
528
|
+
if ti.rmi_methods:
|
|
529
|
+
ti.rmi_methods.clear()
|
|
530
|
+
if ti.rmi_timing:
|
|
531
|
+
ti.rmi_timing.clear()
|
|
532
|
+
self._start_time = time.time()
|
|
533
|
+
|
|
534
|
+
def clear_task(self, task_id: str) -> bool:
|
|
535
|
+
"""특정 Task의 성능 데이터 초기화
|
|
536
|
+
|
|
537
|
+
Args:
|
|
538
|
+
task_id: Task 식별자
|
|
539
|
+
|
|
540
|
+
Returns:
|
|
541
|
+
성공 여부
|
|
542
|
+
"""
|
|
543
|
+
ti = self._tasks.get(task_id)
|
|
544
|
+
if not ti:
|
|
545
|
+
return False
|
|
546
|
+
if ti.rmi_methods:
|
|
547
|
+
ti.rmi_methods.clear()
|
|
548
|
+
if ti.rmi_timing:
|
|
549
|
+
ti.rmi_timing.clear()
|
|
550
|
+
return True
|