kailash 0.1.4__py3-none-any.whl → 0.2.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.
- kailash/__init__.py +1 -1
- kailash/access_control.py +740 -0
- kailash/api/__main__.py +6 -0
- kailash/api/auth.py +668 -0
- kailash/api/custom_nodes.py +285 -0
- kailash/api/custom_nodes_secure.py +377 -0
- kailash/api/database.py +620 -0
- kailash/api/studio.py +915 -0
- kailash/api/studio_secure.py +893 -0
- kailash/mcp/__init__.py +53 -0
- kailash/mcp/__main__.py +13 -0
- kailash/mcp/ai_registry_server.py +712 -0
- kailash/mcp/client.py +447 -0
- kailash/mcp/client_new.py +334 -0
- kailash/mcp/server.py +293 -0
- kailash/mcp/server_new.py +336 -0
- kailash/mcp/servers/__init__.py +12 -0
- kailash/mcp/servers/ai_registry.py +289 -0
- kailash/nodes/__init__.py +4 -2
- kailash/nodes/ai/__init__.py +38 -0
- kailash/nodes/ai/a2a.py +1790 -0
- kailash/nodes/ai/agents.py +116 -2
- kailash/nodes/ai/ai_providers.py +206 -8
- kailash/nodes/ai/intelligent_agent_orchestrator.py +2108 -0
- kailash/nodes/ai/iterative_llm_agent.py +1280 -0
- kailash/nodes/ai/llm_agent.py +324 -1
- kailash/nodes/ai/self_organizing.py +1623 -0
- kailash/nodes/api/http.py +106 -25
- kailash/nodes/api/rest.py +116 -21
- kailash/nodes/base.py +15 -2
- kailash/nodes/base_async.py +45 -0
- kailash/nodes/base_cycle_aware.py +374 -0
- kailash/nodes/base_with_acl.py +338 -0
- kailash/nodes/code/python.py +135 -27
- kailash/nodes/data/readers.py +116 -53
- kailash/nodes/data/writers.py +16 -6
- kailash/nodes/logic/__init__.py +8 -0
- kailash/nodes/logic/async_operations.py +48 -9
- kailash/nodes/logic/convergence.py +642 -0
- kailash/nodes/logic/loop.py +153 -0
- kailash/nodes/logic/operations.py +212 -27
- kailash/nodes/logic/workflow.py +26 -18
- kailash/nodes/mixins/__init__.py +11 -0
- kailash/nodes/mixins/mcp.py +228 -0
- kailash/nodes/mixins.py +387 -0
- kailash/nodes/transform/__init__.py +8 -1
- kailash/nodes/transform/processors.py +119 -4
- kailash/runtime/__init__.py +2 -1
- kailash/runtime/access_controlled.py +458 -0
- kailash/runtime/local.py +106 -33
- kailash/runtime/parallel_cyclic.py +529 -0
- kailash/sdk_exceptions.py +90 -5
- kailash/security.py +845 -0
- kailash/tracking/manager.py +38 -15
- kailash/tracking/models.py +1 -1
- kailash/tracking/storage/filesystem.py +30 -2
- kailash/utils/__init__.py +8 -0
- kailash/workflow/__init__.py +18 -0
- kailash/workflow/convergence.py +270 -0
- kailash/workflow/cycle_analyzer.py +768 -0
- kailash/workflow/cycle_builder.py +573 -0
- kailash/workflow/cycle_config.py +709 -0
- kailash/workflow/cycle_debugger.py +760 -0
- kailash/workflow/cycle_exceptions.py +601 -0
- kailash/workflow/cycle_profiler.py +671 -0
- kailash/workflow/cycle_state.py +338 -0
- kailash/workflow/cyclic_runner.py +985 -0
- kailash/workflow/graph.py +500 -39
- kailash/workflow/migration.py +768 -0
- kailash/workflow/safety.py +365 -0
- kailash/workflow/templates.py +744 -0
- kailash/workflow/validation.py +693 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/METADATA +446 -13
- kailash-0.2.0.dist-info/RECORD +125 -0
- kailash/nodes/mcp/__init__.py +0 -11
- kailash/nodes/mcp/client.py +0 -554
- kailash/nodes/mcp/resource.py +0 -682
- kailash/nodes/mcp/server.py +0 -577
- kailash-0.1.4.dist-info/RECORD +0 -85
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/WHEEL +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,671 @@
|
|
1
|
+
"""
|
2
|
+
Advanced Performance Profiling and Analysis for Cyclic Workflows.
|
3
|
+
|
4
|
+
This module provides comprehensive performance profiling capabilities for cyclic
|
5
|
+
workflows, including detailed statistical analysis, resource usage monitoring,
|
6
|
+
bottleneck identification, and automated optimization recommendations based on
|
7
|
+
execution patterns and performance characteristics.
|
8
|
+
|
9
|
+
Examples:
|
10
|
+
Basic profiling setup:
|
11
|
+
|
12
|
+
>>> profiler = CycleProfiler(enable_advanced_metrics=True)
|
13
|
+
>>> # Add execution traces
|
14
|
+
>>> profiler.add_trace(execution_trace)
|
15
|
+
>>> # Analyze performance
|
16
|
+
>>> metrics = profiler.analyze_performance()
|
17
|
+
>>> print(f"Overall efficiency: {metrics.efficiency_score}")
|
18
|
+
|
19
|
+
Comparative analysis:
|
20
|
+
|
21
|
+
>>> # Compare multiple cycles
|
22
|
+
>>> comparison = profiler.compare_cycles(["cycle_1", "cycle_2", "cycle_3"])
|
23
|
+
>>> print(f"Best cycle: {comparison['best_cycle']}")
|
24
|
+
>>> print(f"Performance gaps: {comparison['significant_differences']}")
|
25
|
+
|
26
|
+
Optimization recommendations:
|
27
|
+
|
28
|
+
>>> # Get actionable recommendations
|
29
|
+
>>> recommendations = profiler.get_optimization_recommendations()
|
30
|
+
>>> for rec in recommendations:
|
31
|
+
... print(f"{rec['priority']}: {rec['description']}")
|
32
|
+
... print(f" Suggestion: {rec['suggestion']}")
|
33
|
+
... print(f" Current: {rec['current_value']}")
|
34
|
+
... print(f" Target: {rec['target_improvement']}")
|
35
|
+
|
36
|
+
Comprehensive reporting:
|
37
|
+
|
38
|
+
>>> # Generate detailed report
|
39
|
+
>>> report = profiler.generate_performance_report()
|
40
|
+
>>> # Export for external analysis
|
41
|
+
>>> profiler.export_profile_data("performance_analysis.json")
|
42
|
+
"""
|
43
|
+
|
44
|
+
import logging
|
45
|
+
import statistics
|
46
|
+
from dataclasses import dataclass, field
|
47
|
+
from datetime import datetime
|
48
|
+
from typing import Any, Dict, List, Optional, Tuple
|
49
|
+
|
50
|
+
from kailash.workflow.cycle_debugger import CycleExecutionTrace
|
51
|
+
|
52
|
+
logger = logging.getLogger(__name__)
|
53
|
+
|
54
|
+
|
55
|
+
@dataclass
|
56
|
+
class PerformanceMetrics:
|
57
|
+
"""
|
58
|
+
Comprehensive performance metrics for cycle analysis.
|
59
|
+
|
60
|
+
This class aggregates and analyzes performance data from cycle executions,
|
61
|
+
providing detailed insights into timing, resource usage, and efficiency
|
62
|
+
characteristics of cyclic workflows.
|
63
|
+
|
64
|
+
Attributes:
|
65
|
+
total_cycles: Number of cycles analyzed.
|
66
|
+
total_iterations: Total iterations across all cycles.
|
67
|
+
avg_cycle_time: Average cycle execution time.
|
68
|
+
avg_iteration_time: Average iteration execution time.
|
69
|
+
min_iteration_time: Fastest iteration time.
|
70
|
+
max_iteration_time: Slowest iteration time.
|
71
|
+
iteration_time_stddev: Standard deviation of iteration times.
|
72
|
+
memory_stats: Memory usage statistics.
|
73
|
+
cpu_stats: CPU usage statistics.
|
74
|
+
convergence_stats: Convergence analysis.
|
75
|
+
bottlenecks: Identified performance bottlenecks.
|
76
|
+
optimization_opportunities: Suggested optimizations.
|
77
|
+
"""
|
78
|
+
|
79
|
+
total_cycles: int = 0
|
80
|
+
total_iterations: int = 0
|
81
|
+
avg_cycle_time: float = 0.0
|
82
|
+
avg_iteration_time: float = 0.0
|
83
|
+
min_iteration_time: float = float('inf')
|
84
|
+
max_iteration_time: float = 0.0
|
85
|
+
iteration_time_stddev: float = 0.0
|
86
|
+
memory_stats: Dict[str, float] = field(default_factory=dict)
|
87
|
+
cpu_stats: Dict[str, float] = field(default_factory=dict)
|
88
|
+
convergence_stats: Dict[str, Any] = field(default_factory=dict)
|
89
|
+
bottlenecks: List[str] = field(default_factory=list)
|
90
|
+
optimization_opportunities: List[str] = field(default_factory=list)
|
91
|
+
|
92
|
+
def to_dict(self) -> Dict[str, Any]:
|
93
|
+
"""Convert metrics to dictionary for serialization.
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
Dictionary representation of performance metrics.
|
97
|
+
"""
|
98
|
+
return {
|
99
|
+
"total_cycles": self.total_cycles,
|
100
|
+
"total_iterations": self.total_iterations,
|
101
|
+
"timing": {
|
102
|
+
"avg_cycle_time": self.avg_cycle_time,
|
103
|
+
"avg_iteration_time": self.avg_iteration_time,
|
104
|
+
"min_iteration_time": self.min_iteration_time if self.min_iteration_time != float('inf') else 0.0,
|
105
|
+
"max_iteration_time": self.max_iteration_time,
|
106
|
+
"iteration_time_stddev": self.iteration_time_stddev
|
107
|
+
},
|
108
|
+
"memory_stats": self.memory_stats,
|
109
|
+
"cpu_stats": self.cpu_stats,
|
110
|
+
"convergence_stats": self.convergence_stats,
|
111
|
+
"bottlenecks": self.bottlenecks,
|
112
|
+
"optimization_opportunities": self.optimization_opportunities
|
113
|
+
}
|
114
|
+
|
115
|
+
|
116
|
+
class CycleProfiler:
|
117
|
+
"""
|
118
|
+
Advanced profiling and performance analysis for cyclic workflows.
|
119
|
+
|
120
|
+
This class provides comprehensive performance analysis capabilities for
|
121
|
+
cycles, including statistical analysis, bottleneck identification,
|
122
|
+
comparative analysis across multiple cycles, and detailed optimization
|
123
|
+
recommendations based on execution patterns.
|
124
|
+
|
125
|
+
Examples:
|
126
|
+
>>> profiler = CycleProfiler()
|
127
|
+
>>> profiler.add_trace(execution_trace)
|
128
|
+
>>> metrics = profiler.analyze_performance()
|
129
|
+
>>> recommendations = profiler.get_optimization_recommendations()
|
130
|
+
"""
|
131
|
+
|
132
|
+
def __init__(self, enable_advanced_metrics: bool = True):
|
133
|
+
"""
|
134
|
+
Initialize cycle profiler.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
enable_advanced_metrics: Whether to enable advanced statistical analysis.
|
138
|
+
"""
|
139
|
+
self.enable_advanced_metrics = enable_advanced_metrics
|
140
|
+
self.traces: List[CycleExecutionTrace] = []
|
141
|
+
self.performance_history: List[PerformanceMetrics] = []
|
142
|
+
|
143
|
+
def add_trace(self, trace: CycleExecutionTrace):
|
144
|
+
"""
|
145
|
+
Add a cycle execution trace for analysis.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
trace: Completed execution trace to analyze.
|
149
|
+
|
150
|
+
Examples:
|
151
|
+
>>> profiler.add_trace(execution_trace)
|
152
|
+
"""
|
153
|
+
self.traces.append(trace)
|
154
|
+
logger.debug(f"Added trace for cycle '{trace.cycle_id}' with {len(trace.iterations)} iterations")
|
155
|
+
|
156
|
+
def analyze_performance(self) -> PerformanceMetrics:
|
157
|
+
"""
|
158
|
+
Perform comprehensive performance analysis on all traces.
|
159
|
+
|
160
|
+
Analyzes all collected traces to generate comprehensive performance
|
161
|
+
metrics, identify bottlenecks, and provide optimization recommendations
|
162
|
+
based on statistical analysis of execution patterns.
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
Comprehensive performance analysis results.
|
166
|
+
|
167
|
+
Examples:
|
168
|
+
>>> metrics = profiler.analyze_performance()
|
169
|
+
>>> print(f"Average cycle time: {metrics.avg_cycle_time:.3f}s")
|
170
|
+
"""
|
171
|
+
if not self.traces:
|
172
|
+
logger.warning("No traces available for performance analysis")
|
173
|
+
return PerformanceMetrics()
|
174
|
+
|
175
|
+
# Collect all timing data
|
176
|
+
cycle_times = []
|
177
|
+
iteration_times = []
|
178
|
+
memory_values = []
|
179
|
+
cpu_values = []
|
180
|
+
convergence_data = []
|
181
|
+
|
182
|
+
for trace in self.traces:
|
183
|
+
if trace.total_execution_time:
|
184
|
+
cycle_times.append(trace.total_execution_time)
|
185
|
+
|
186
|
+
for iteration in trace.iterations:
|
187
|
+
if iteration.execution_time:
|
188
|
+
iteration_times.append(iteration.execution_time)
|
189
|
+
if iteration.memory_usage_mb:
|
190
|
+
memory_values.append(iteration.memory_usage_mb)
|
191
|
+
if iteration.cpu_usage_percent:
|
192
|
+
cpu_values.append(iteration.cpu_usage_percent)
|
193
|
+
if iteration.convergence_value:
|
194
|
+
convergence_data.append(iteration.convergence_value)
|
195
|
+
|
196
|
+
# Calculate basic metrics
|
197
|
+
metrics = PerformanceMetrics(
|
198
|
+
total_cycles=len(self.traces),
|
199
|
+
total_iterations=sum(len(trace.iterations) for trace in self.traces)
|
200
|
+
)
|
201
|
+
|
202
|
+
# Timing analysis
|
203
|
+
if cycle_times:
|
204
|
+
metrics.avg_cycle_time = statistics.mean(cycle_times)
|
205
|
+
|
206
|
+
if iteration_times:
|
207
|
+
metrics.avg_iteration_time = statistics.mean(iteration_times)
|
208
|
+
metrics.min_iteration_time = min(iteration_times)
|
209
|
+
metrics.max_iteration_time = max(iteration_times)
|
210
|
+
if len(iteration_times) > 1:
|
211
|
+
metrics.iteration_time_stddev = statistics.stdev(iteration_times)
|
212
|
+
|
213
|
+
# Memory analysis
|
214
|
+
if memory_values:
|
215
|
+
metrics.memory_stats = {
|
216
|
+
"avg": statistics.mean(memory_values),
|
217
|
+
"min": min(memory_values),
|
218
|
+
"max": max(memory_values),
|
219
|
+
"stddev": statistics.stdev(memory_values) if len(memory_values) > 1 else 0.0,
|
220
|
+
"median": statistics.median(memory_values)
|
221
|
+
}
|
222
|
+
|
223
|
+
# CPU analysis
|
224
|
+
if cpu_values:
|
225
|
+
metrics.cpu_stats = {
|
226
|
+
"avg": statistics.mean(cpu_values),
|
227
|
+
"min": min(cpu_values),
|
228
|
+
"max": max(cpu_values),
|
229
|
+
"stddev": statistics.stdev(cpu_values) if len(cpu_values) > 1 else 0.0,
|
230
|
+
"median": statistics.median(cpu_values)
|
231
|
+
}
|
232
|
+
|
233
|
+
# Convergence analysis
|
234
|
+
if convergence_data:
|
235
|
+
metrics.convergence_stats = self._analyze_convergence_performance(convergence_data)
|
236
|
+
|
237
|
+
# Advanced analysis if enabled
|
238
|
+
if self.enable_advanced_metrics:
|
239
|
+
metrics.bottlenecks = self._identify_bottlenecks(metrics)
|
240
|
+
metrics.optimization_opportunities = self._identify_optimizations(metrics)
|
241
|
+
|
242
|
+
# Store in history
|
243
|
+
self.performance_history.append(metrics)
|
244
|
+
|
245
|
+
logger.info(
|
246
|
+
f"Analyzed performance for {metrics.total_cycles} cycles, "
|
247
|
+
f"{metrics.total_iterations} iterations, "
|
248
|
+
f"avg cycle time: {metrics.avg_cycle_time:.3f}s"
|
249
|
+
)
|
250
|
+
|
251
|
+
return metrics
|
252
|
+
|
253
|
+
def compare_cycles(self, cycle_ids: List[str]) -> Dict[str, Any]:
|
254
|
+
"""
|
255
|
+
Compare performance across multiple specific cycles.
|
256
|
+
|
257
|
+
Provides detailed comparative analysis between specific cycles,
|
258
|
+
highlighting performance differences, convergence patterns, and
|
259
|
+
relative efficiency metrics.
|
260
|
+
|
261
|
+
Args:
|
262
|
+
cycle_ids (List[str]): List of cycle IDs to compare
|
263
|
+
|
264
|
+
Returns:
|
265
|
+
Dict[str, Any]: Comparative analysis results
|
266
|
+
|
267
|
+
Side Effects:
|
268
|
+
None - this is a pure analysis method
|
269
|
+
|
270
|
+
Example:
|
271
|
+
>>> comparison = profiler.compare_cycles(["cycle_1", "cycle_2"])
|
272
|
+
>>> print(f"Best performing cycle: {comparison['best_cycle']}")
|
273
|
+
"""
|
274
|
+
relevant_traces = [trace for trace in self.traces if trace.cycle_id in cycle_ids]
|
275
|
+
|
276
|
+
if len(relevant_traces) < 2:
|
277
|
+
return {"error": "Need at least 2 cycles for comparison"}
|
278
|
+
|
279
|
+
comparison = {
|
280
|
+
"cycles_compared": len(relevant_traces),
|
281
|
+
"cycle_details": {},
|
282
|
+
"performance_ranking": [],
|
283
|
+
"significant_differences": []
|
284
|
+
}
|
285
|
+
|
286
|
+
# Analyze each cycle
|
287
|
+
for trace in relevant_traces:
|
288
|
+
stats = trace.get_statistics()
|
289
|
+
|
290
|
+
comparison["cycle_details"][trace.cycle_id] = {
|
291
|
+
"execution_time": trace.total_execution_time,
|
292
|
+
"iterations": len(trace.iterations),
|
293
|
+
"converged": trace.converged,
|
294
|
+
"efficiency_score": stats["efficiency_score"],
|
295
|
+
"avg_iteration_time": stats["avg_iteration_time"],
|
296
|
+
"convergence_rate": stats["convergence_rate"]
|
297
|
+
}
|
298
|
+
|
299
|
+
# Rank by efficiency score
|
300
|
+
ranking = sorted(
|
301
|
+
comparison["cycle_details"].items(),
|
302
|
+
key=lambda x: x[1]["efficiency_score"],
|
303
|
+
reverse=True
|
304
|
+
)
|
305
|
+
comparison["performance_ranking"] = [cycle_id for cycle_id, _ in ranking]
|
306
|
+
comparison["best_cycle"] = ranking[0][0] if ranking else None
|
307
|
+
comparison["worst_cycle"] = ranking[-1][0] if ranking else None
|
308
|
+
|
309
|
+
# Identify significant differences
|
310
|
+
if len(ranking) >= 2:
|
311
|
+
best_score = ranking[0][1]["efficiency_score"]
|
312
|
+
worst_score = ranking[-1][1]["efficiency_score"]
|
313
|
+
|
314
|
+
if best_score - worst_score > 0.2:
|
315
|
+
comparison["significant_differences"].append(
|
316
|
+
f"Large efficiency gap: {best_score:.2f} vs {worst_score:.2f}"
|
317
|
+
)
|
318
|
+
|
319
|
+
# Compare convergence rates
|
320
|
+
convergence_rates = [details["convergence_rate"] for _, details in ranking]
|
321
|
+
if max(convergence_rates) - min(convergence_rates) > 0.3:
|
322
|
+
comparison["significant_differences"].append(
|
323
|
+
"Significant variation in convergence rates"
|
324
|
+
)
|
325
|
+
|
326
|
+
return comparison
|
327
|
+
|
328
|
+
def get_optimization_recommendations(self, trace: Optional[CycleExecutionTrace] = None) -> List[Dict[str, Any]]:
|
329
|
+
"""
|
330
|
+
Generate detailed optimization recommendations.
|
331
|
+
|
332
|
+
Provides specific, actionable optimization recommendations based on
|
333
|
+
performance analysis, including parameter tuning suggestions,
|
334
|
+
algorithmic improvements, and resource optimization strategies.
|
335
|
+
|
336
|
+
Args:
|
337
|
+
trace (Optional[CycleExecutionTrace]): Specific trace to analyze, or None for overall recommendations
|
338
|
+
|
339
|
+
Returns:
|
340
|
+
List[Dict[str, Any]]: List of optimization recommendations with details
|
341
|
+
|
342
|
+
Side Effects:
|
343
|
+
None - this is a pure analysis method
|
344
|
+
|
345
|
+
Example:
|
346
|
+
>>> recommendations = profiler.get_optimization_recommendations()
|
347
|
+
>>> for rec in recommendations:
|
348
|
+
... print(f"{rec['priority']}: {rec['description']}")
|
349
|
+
"""
|
350
|
+
recommendations = []
|
351
|
+
|
352
|
+
if trace:
|
353
|
+
traces_to_analyze = [trace]
|
354
|
+
else:
|
355
|
+
traces_to_analyze = self.traces
|
356
|
+
|
357
|
+
if not traces_to_analyze:
|
358
|
+
return recommendations
|
359
|
+
|
360
|
+
# Analyze all traces for patterns
|
361
|
+
self.analyze_performance() if not trace else None
|
362
|
+
|
363
|
+
for target_trace in traces_to_analyze:
|
364
|
+
stats = target_trace.get_statistics()
|
365
|
+
|
366
|
+
# Efficiency recommendations
|
367
|
+
if stats["efficiency_score"] < 0.3:
|
368
|
+
recommendations.append({
|
369
|
+
"priority": "HIGH",
|
370
|
+
"category": "efficiency",
|
371
|
+
"description": "Very low efficiency detected",
|
372
|
+
"suggestion": "Consider reducing max_iterations or improving convergence condition",
|
373
|
+
"cycle_id": target_trace.cycle_id,
|
374
|
+
"current_value": stats["efficiency_score"],
|
375
|
+
"target_improvement": "Increase to > 0.5"
|
376
|
+
})
|
377
|
+
|
378
|
+
# Convergence recommendations
|
379
|
+
if not target_trace.converged:
|
380
|
+
if stats["total_iterations"] >= (target_trace.max_iterations_configured or 0):
|
381
|
+
recommendations.append({
|
382
|
+
"priority": "HIGH",
|
383
|
+
"category": "convergence",
|
384
|
+
"description": "Cycle reached max_iterations without converging",
|
385
|
+
"suggestion": "Increase max_iterations or improve algorithm",
|
386
|
+
"cycle_id": target_trace.cycle_id,
|
387
|
+
"current_value": target_trace.max_iterations_configured,
|
388
|
+
"target_improvement": f"Increase to {int((target_trace.max_iterations_configured or 10) * 1.5)}"
|
389
|
+
})
|
390
|
+
|
391
|
+
# Performance recommendations
|
392
|
+
if stats["avg_iteration_time"] > 0.5:
|
393
|
+
recommendations.append({
|
394
|
+
"priority": "MEDIUM",
|
395
|
+
"category": "performance",
|
396
|
+
"description": "High average iteration time",
|
397
|
+
"suggestion": "Optimize node execution or reduce data processing",
|
398
|
+
"cycle_id": target_trace.cycle_id,
|
399
|
+
"current_value": stats["avg_iteration_time"],
|
400
|
+
"target_improvement": "Reduce to < 0.5s per iteration"
|
401
|
+
})
|
402
|
+
|
403
|
+
# Memory recommendations
|
404
|
+
if target_trace.memory_peak_mb and target_trace.memory_peak_mb > 1000:
|
405
|
+
recommendations.append({
|
406
|
+
"priority": "MEDIUM",
|
407
|
+
"category": "memory",
|
408
|
+
"description": "High memory usage detected",
|
409
|
+
"suggestion": "Consider data streaming, chunking, or garbage collection",
|
410
|
+
"cycle_id": target_trace.cycle_id,
|
411
|
+
"current_value": target_trace.memory_peak_mb,
|
412
|
+
"target_improvement": "Reduce to < 1000 MB"
|
413
|
+
})
|
414
|
+
|
415
|
+
# Convergence pattern recommendations
|
416
|
+
convergence_trend = target_trace.get_convergence_trend()
|
417
|
+
if convergence_trend:
|
418
|
+
pattern_analysis = self._analyze_convergence_pattern(convergence_trend)
|
419
|
+
if pattern_analysis["unstable"]:
|
420
|
+
recommendations.append({
|
421
|
+
"priority": "HIGH",
|
422
|
+
"category": "stability",
|
423
|
+
"description": "Unstable convergence pattern detected",
|
424
|
+
"suggestion": "Reduce learning rate or add regularization",
|
425
|
+
"cycle_id": target_trace.cycle_id,
|
426
|
+
"current_value": "Unstable",
|
427
|
+
"target_improvement": "Stable convergence"
|
428
|
+
})
|
429
|
+
|
430
|
+
# Sort by priority
|
431
|
+
priority_order = {"HIGH": 0, "MEDIUM": 1, "LOW": 2}
|
432
|
+
recommendations.sort(key=lambda x: priority_order.get(x["priority"], 3))
|
433
|
+
|
434
|
+
return recommendations
|
435
|
+
|
436
|
+
def generate_performance_report(self) -> Dict[str, Any]:
|
437
|
+
"""
|
438
|
+
Generate comprehensive performance report.
|
439
|
+
|
440
|
+
Creates a detailed performance report including metrics analysis,
|
441
|
+
recommendations, trends, and comparative insights across all
|
442
|
+
analyzed cycles.
|
443
|
+
|
444
|
+
Returns:
|
445
|
+
Dict[str, Any]: Comprehensive performance report
|
446
|
+
|
447
|
+
Side Effects:
|
448
|
+
None - this is a pure analysis method
|
449
|
+
|
450
|
+
Example:
|
451
|
+
>>> report = profiler.generate_performance_report()
|
452
|
+
>>> print(f"Overall score: {report['overall_score']}")
|
453
|
+
"""
|
454
|
+
metrics = self.analyze_performance()
|
455
|
+
|
456
|
+
# Calculate overall performance score
|
457
|
+
overall_score = self._calculate_overall_score(metrics)
|
458
|
+
|
459
|
+
# Generate trend analysis if we have history
|
460
|
+
trend_analysis = self._analyze_performance_trends() if len(self.performance_history) > 1 else None
|
461
|
+
|
462
|
+
# Get top recommendations
|
463
|
+
recommendations = self.get_optimization_recommendations()
|
464
|
+
|
465
|
+
report = {
|
466
|
+
"summary": {
|
467
|
+
"overall_score": overall_score,
|
468
|
+
"total_cycles_analyzed": metrics.total_cycles,
|
469
|
+
"total_iterations": metrics.total_iterations,
|
470
|
+
"avg_cycle_time": metrics.avg_cycle_time,
|
471
|
+
"primary_bottlenecks": metrics.bottlenecks[:3]
|
472
|
+
},
|
473
|
+
"detailed_metrics": metrics.to_dict(),
|
474
|
+
"trend_analysis": trend_analysis,
|
475
|
+
"recommendations": recommendations[:10], # Top 10 recommendations
|
476
|
+
"cycle_comparisons": self._get_cycle_comparisons(),
|
477
|
+
"generated_at": datetime.now().isoformat()
|
478
|
+
}
|
479
|
+
|
480
|
+
return report
|
481
|
+
|
482
|
+
def export_profile_data(self, filepath: str, format: str = "json"):
|
483
|
+
"""
|
484
|
+
Export profiling data for external analysis.
|
485
|
+
|
486
|
+
Args:
|
487
|
+
filepath (str): Output file path
|
488
|
+
format (str): Export format ("json", "csv")
|
489
|
+
|
490
|
+
Side Effects:
|
491
|
+
Creates file with profiling data
|
492
|
+
|
493
|
+
Example:
|
494
|
+
>>> profiler.export_profile_data("profile_analysis.json")
|
495
|
+
"""
|
496
|
+
report = self.generate_performance_report()
|
497
|
+
|
498
|
+
if format == "json":
|
499
|
+
import json
|
500
|
+
with open(filepath, 'w') as f:
|
501
|
+
json.dump(report, f, indent=2)
|
502
|
+
elif format == "csv":
|
503
|
+
import csv
|
504
|
+
with open(filepath, 'w', newline='') as f:
|
505
|
+
writer = csv.writer(f)
|
506
|
+
|
507
|
+
# Write summary data
|
508
|
+
writer.writerow(["Metric", "Value"])
|
509
|
+
writer.writerow(["Overall Score", report["summary"]["overall_score"]])
|
510
|
+
writer.writerow(["Total Cycles", report["summary"]["total_cycles_analyzed"]])
|
511
|
+
writer.writerow(["Avg Cycle Time", report["summary"]["avg_cycle_time"]])
|
512
|
+
writer.writerow([])
|
513
|
+
|
514
|
+
# Write detailed metrics
|
515
|
+
writer.writerow(["Detailed Metrics"])
|
516
|
+
metrics = report["detailed_metrics"]
|
517
|
+
for key, value in metrics.items():
|
518
|
+
if isinstance(value, dict):
|
519
|
+
for sub_key, sub_value in value.items():
|
520
|
+
writer.writerow([f"{key}_{sub_key}", sub_value])
|
521
|
+
else:
|
522
|
+
writer.writerow([key, value])
|
523
|
+
else:
|
524
|
+
raise ValueError(f"Unsupported export format: {format}")
|
525
|
+
|
526
|
+
logger.info(f"Exported profiling data to {filepath} in {format} format")
|
527
|
+
|
528
|
+
def _analyze_convergence_performance(self, convergence_data: List[float]) -> Dict[str, Any]:
|
529
|
+
"""Analyze convergence performance characteristics."""
|
530
|
+
if not convergence_data:
|
531
|
+
return {}
|
532
|
+
|
533
|
+
return {
|
534
|
+
"avg_convergence": statistics.mean(convergence_data),
|
535
|
+
"min_convergence": min(convergence_data),
|
536
|
+
"max_convergence": max(convergence_data),
|
537
|
+
"convergence_stddev": statistics.stdev(convergence_data) if len(convergence_data) > 1 else 0.0,
|
538
|
+
"convergence_trend": "improving" if convergence_data[0] > convergence_data[-1] else "degrading",
|
539
|
+
"data_points": len(convergence_data)
|
540
|
+
}
|
541
|
+
|
542
|
+
def _analyze_convergence_pattern(self, convergence_trend: List[Tuple[int, Optional[float]]]) -> Dict[str, Any]:
|
543
|
+
"""Analyze convergence pattern for stability."""
|
544
|
+
valid_points = [value for _, value in convergence_trend if value is not None]
|
545
|
+
|
546
|
+
if len(valid_points) < 3:
|
547
|
+
return {"unstable": False, "reason": "insufficient_data"}
|
548
|
+
|
549
|
+
# Calculate volatility
|
550
|
+
differences = [abs(valid_points[i] - valid_points[i-1]) for i in range(1, len(valid_points))]
|
551
|
+
avg_difference = statistics.mean(differences)
|
552
|
+
max_difference = max(differences)
|
553
|
+
|
554
|
+
# Consider unstable if large swings or high volatility
|
555
|
+
unstable = max_difference > (2 * avg_difference) and avg_difference > 0.1
|
556
|
+
|
557
|
+
return {
|
558
|
+
"unstable": unstable,
|
559
|
+
"avg_volatility": avg_difference,
|
560
|
+
"max_volatility": max_difference,
|
561
|
+
"reason": "high_volatility" if unstable else "stable"
|
562
|
+
}
|
563
|
+
|
564
|
+
def _identify_bottlenecks(self, metrics: PerformanceMetrics) -> List[str]:
|
565
|
+
"""Identify performance bottlenecks from metrics."""
|
566
|
+
bottlenecks = []
|
567
|
+
|
568
|
+
# High iteration time variance suggests inconsistent performance
|
569
|
+
if metrics.iteration_time_stddev > metrics.avg_iteration_time * 0.5:
|
570
|
+
bottlenecks.append("High iteration time variance - inconsistent node performance")
|
571
|
+
|
572
|
+
# Very slow iterations
|
573
|
+
if metrics.max_iteration_time > metrics.avg_iteration_time * 3:
|
574
|
+
bottlenecks.append("Outlier slow iterations detected - potential resource contention")
|
575
|
+
|
576
|
+
# Memory bottlenecks
|
577
|
+
if metrics.memory_stats and metrics.memory_stats.get("max", 0) > 2000:
|
578
|
+
bottlenecks.append("High memory usage - potential memory leaks or inefficient data handling")
|
579
|
+
|
580
|
+
# CPU bottlenecks
|
581
|
+
if metrics.cpu_stats and metrics.cpu_stats.get("avg", 0) > 80:
|
582
|
+
bottlenecks.append("High CPU usage - computationally intensive operations")
|
583
|
+
|
584
|
+
return bottlenecks
|
585
|
+
|
586
|
+
def _identify_optimizations(self, metrics: PerformanceMetrics) -> List[str]:
|
587
|
+
"""Identify optimization opportunities."""
|
588
|
+
optimizations = []
|
589
|
+
|
590
|
+
# Low convergence rate suggests early termination opportunities
|
591
|
+
convergence_rate = metrics.convergence_stats.get("avg_convergence")
|
592
|
+
if convergence_rate and convergence_rate < 0.5:
|
593
|
+
optimizations.append("Add early termination conditions for faster convergence")
|
594
|
+
|
595
|
+
# High memory variance suggests optimization potential
|
596
|
+
if metrics.memory_stats and metrics.memory_stats.get("stddev", 0) > 100:
|
597
|
+
optimizations.append("Optimize memory usage patterns for consistency")
|
598
|
+
|
599
|
+
# Slow average iteration time
|
600
|
+
if metrics.avg_iteration_time > 0.1:
|
601
|
+
optimizations.append("Optimize node execution performance")
|
602
|
+
|
603
|
+
return optimizations
|
604
|
+
|
605
|
+
def _calculate_overall_score(self, metrics: PerformanceMetrics) -> float:
|
606
|
+
"""Calculate overall performance score (0-1, higher is better)."""
|
607
|
+
score_components = []
|
608
|
+
|
609
|
+
# Efficiency component (convergence rate)
|
610
|
+
if metrics.convergence_stats:
|
611
|
+
avg_convergence = metrics.convergence_stats.get("avg_convergence", 0.5)
|
612
|
+
score_components.append(min(1.0, avg_convergence))
|
613
|
+
|
614
|
+
# Speed component (based on iteration time)
|
615
|
+
if metrics.avg_iteration_time > 0:
|
616
|
+
speed_score = max(0.0, 1.0 - min(1.0, metrics.avg_iteration_time / 2.0))
|
617
|
+
score_components.append(speed_score)
|
618
|
+
|
619
|
+
# Consistency component (low variance is good)
|
620
|
+
if metrics.iteration_time_stddev >= 0:
|
621
|
+
consistency_score = max(0.0, 1.0 - min(1.0, metrics.iteration_time_stddev / metrics.avg_iteration_time if metrics.avg_iteration_time > 0 else 0))
|
622
|
+
score_components.append(consistency_score)
|
623
|
+
|
624
|
+
# Memory efficiency component
|
625
|
+
if metrics.memory_stats:
|
626
|
+
max_memory = metrics.memory_stats.get("max", 500)
|
627
|
+
memory_score = max(0.0, 1.0 - min(1.0, max_memory / 2000)) # Penalty after 2GB
|
628
|
+
score_components.append(memory_score)
|
629
|
+
|
630
|
+
return statistics.mean(score_components) if score_components else 0.5
|
631
|
+
|
632
|
+
def _analyze_performance_trends(self) -> Dict[str, Any]:
|
633
|
+
"""Analyze performance trends over time."""
|
634
|
+
if len(self.performance_history) < 2:
|
635
|
+
return {"trend": "insufficient_data"}
|
636
|
+
|
637
|
+
recent_scores = [self._calculate_overall_score(m) for m in self.performance_history[-5:]]
|
638
|
+
|
639
|
+
if len(recent_scores) >= 2:
|
640
|
+
trend = "improving" if recent_scores[-1] > recent_scores[0] else "degrading"
|
641
|
+
trend_strength = abs(recent_scores[-1] - recent_scores[0])
|
642
|
+
else:
|
643
|
+
trend = "stable"
|
644
|
+
trend_strength = 0.0
|
645
|
+
|
646
|
+
return {
|
647
|
+
"trend": trend,
|
648
|
+
"trend_strength": trend_strength,
|
649
|
+
"recent_scores": recent_scores,
|
650
|
+
"performance_history_length": len(self.performance_history)
|
651
|
+
}
|
652
|
+
|
653
|
+
def _get_cycle_comparisons(self) -> Dict[str, Any]:
|
654
|
+
"""Get comparative analysis across all cycles."""
|
655
|
+
if len(self.traces) < 2:
|
656
|
+
return {"comparison": "insufficient_data"}
|
657
|
+
|
658
|
+
cycle_scores = {}
|
659
|
+
for trace in self.traces:
|
660
|
+
stats = trace.get_statistics()
|
661
|
+
cycle_scores[trace.cycle_id] = stats["efficiency_score"]
|
662
|
+
|
663
|
+
best_cycle = max(cycle_scores.items(), key=lambda x: x[1])
|
664
|
+
worst_cycle = min(cycle_scores.items(), key=lambda x: x[1])
|
665
|
+
|
666
|
+
return {
|
667
|
+
"best_cycle": {"id": best_cycle[0], "score": best_cycle[1]},
|
668
|
+
"worst_cycle": {"id": worst_cycle[0], "score": worst_cycle[1]},
|
669
|
+
"score_range": best_cycle[1] - worst_cycle[1],
|
670
|
+
"total_cycles": len(cycle_scores)
|
671
|
+
}
|