Co-Study4Grid / scripts /profile_diagram_perf.py
github-actions[bot]
Deploy 7688ef1
13d4e44
Raw
History Blame Contribute Delete
5.86 kB
import time
import json
import sys
import os
import argparse
from pathlib import Path
import cProfile
import pstats
import io
# Add the project root to sys.path so we can import expert_backend
project_root = Path(__file__).resolve().parent.parent
sys.path.append(str(project_root))
from expert_backend.services.network_service import network_service
from expert_backend.services.recommender_service import recommender_service
from expert_op4grid_recommender import config as recommender_config
class Profiler:
def __init__(self):
self.timings = {}
self.current_scenario = None
def start_scenario(self, name):
self.current_scenario = name
self.timings[name] = {}
print(f"\n>>> Starting Scenario: {name}")
def time_block(self, name):
return TimerContext(self, name)
class TimerContext:
def __init__(self, profiler, name):
self.profiler = profiler
self.name = name
self.start_time = None
def __enter__(self):
self.start_time = time.perf_counter()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
end_time = time.perf_counter()
elapsed = end_time - self.start_time
self.profiler.timings[self.profiler.current_scenario][self.name] = elapsed
print(f" {self.name}: {elapsed:.4f}s")
def run_profiling(config_path, use_cprofile=False):
p = Profiler()
# Load config
with open(config_path, 'r') as f:
config_data = json.load(f)
# --- Scenario 1: Load network & display N-state diagram ---
p.start_scenario("1_InitialLoad_N_Diagram")
with p.time_block("total_scenario_1"):
with p.time_block("update_config"):
from expert_backend.main import ConfigRequest
req = ConfigRequest(**config_data)
# Simulate the /api/config endpoint logic
recommender_service.reset()
network_service.load_network(req.network_path)
recommender_service.update_config(req)
with p.time_block("get_network_diagram"):
with p.time_block("get_base_network"):
n = recommender_service._get_base_network()
with p.time_block("nad_generation"):
# We time the internal _generate_diagram
res = recommender_service.get_network_diagram()
p.timings[p.current_scenario]["svg_size_bytes"] = len(res["svg"])
p.timings[p.current_scenario]["metadata_size_bytes"] = len(json.dumps(res["metadata"]))
print(f" SVG size: {len(res['svg']) / 1024 / 1024:.2f} MB")
# --- Scenario 2: Select contingency ---
contingency_id = "P.SAOL31RONCI"
p.start_scenario("2_ContingencySelection_N1_Diagram")
with p.time_block("total_scenario_2"):
with p.time_block("run_analysis_step1"):
recommender_service.run_analysis_step1(contingency_id)
with p.time_block("get_n1_diagram"):
# This generates the N-1 diagram and computes flow deltas
res_n1 = recommender_service.get_n1_diagram(contingency_id)
p.timings[p.current_scenario]["svg_size_bytes"] = len(res_n1["svg"])
print(f" SVG size: {len(res_n1['svg']) / 1024 / 1024:.2f} MB")
# --- Scenario 3: Simulate manual action ---
# Manual action: "f344b395-9908-43c2-bca0-75c5f298465e_COUCHP6_coupling"
action_id = "f344b395-9908-43c2-bca0-75c5f298465e_COUCHP6_coupling"
p.start_scenario("3_ManualAction_Simulation_Diagram")
with p.time_block("total_scenario_3"):
with p.time_block("simulate_manual_action"):
# This is the heavy simulation part
sim_res = recommender_service.simulate_manual_action(action_id, contingency_id)
with p.time_block("get_action_variant_diagram"):
# This generates the post-action diagram and flow deltas
res_act = recommender_service.get_action_variant_diagram(action_id)
p.timings[p.current_scenario]["svg_size_bytes"] = len(res_act["svg"])
print(f" SVG size: {len(res_act['svg']) / 1024 / 1024:.2f} MB")
# --- Output results ---
output_file = Path("profiling_results.json")
with open(output_file, 'w') as f:
json.dump(p.timings, f, indent=4)
print(f"\nProfiling results saved to {output_file}")
# Summary table
print("\n" + "="*50)
print(f"{'Phase':<40} | {'Time (s)':>8}")
print("-" * 50)
for scenario, data in p.timings.items():
print(f"\nScenario: {scenario}")
for k, v in data.items():
if isinstance(v, float):
print(f" {k:<38} | {v:>8.4f}")
print("="*50)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Profile ExpertAssist diagram generation.")
parser.add_argument("config", help="Path to the config JSON file")
parser.add_argument("--cprofile", action="store_true", help="Run with cProfile")
args = parser.parse_args()
config_path = args.config
if not os.path.exists(config_path):
# Try relative to project root
config_path = str(project_root / args.config)
if not os.path.exists(config_path):
print(f"Error: Config file not found: {args.config}")
sys.exit(1)
if args.cprofile:
print("Running with cProfile...")
pr = cProfile.Profile()
pr.enable()
run_profiling(config_path)
pr.disable()
s = io.StringIO()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats(30)
print(s.getvalue())
prof_file = "profiling.prof"
pr.dump_stats(prof_file)
print(f"Detailed profile saved to {prof_file}")
else:
run_profiling(config_path)