| 50 | |
| 51 | |
| 52 | class CompilePerfAnalyzer: |
| 53 | def __init__(self, trace_json_path: str): |
| 54 | """Parse clang -ftime-trace JSON output.""" |
| 55 | with open(trace_json_path, "r") as f: |
| 56 | self.trace_data = json.load(f) |
| 57 | self.events = self.trace_data.get("traceEvents", []) |
| 58 | |
| 59 | # Build event index for faster lookups |
| 60 | self._build_event_index() |
| 61 | |
| 62 | def _build_event_index(self) -> None: |
| 63 | """Build indices for different event types.""" |
| 64 | self.source_events: list[dict[str, Any]] = [] |
| 65 | self.parse_events: list[dict[str, Any]] = [] |
| 66 | self.instantiate_events: list[dict[str, Any]] = [] |
| 67 | self.phase_events: dict[str, dict[str, Any]] = {} |
| 68 | |
| 69 | # First pass: collect all events |
| 70 | # Second pass: match begin/end pairs for async events |
| 71 | begin_events: dict[int, dict[str, Any]] = {} |
| 72 | |
| 73 | for event in self.events: |
| 74 | name = event.get("name", "") |
| 75 | phase = event.get("ph", "") |
| 76 | |
| 77 | # Handle async begin/end pairs (Source events use this) |
| 78 | if phase == "b": |
| 79 | # Store begin event by its ID |
| 80 | event_id = event.get("id", 0) |
| 81 | begin_events[event_id] = event |
| 82 | continue |
| 83 | elif phase == "e": |
| 84 | # Match with begin event and create complete event |
| 85 | event_id = event.get("id", 0) |
| 86 | if event_id in begin_events: |
| 87 | begin = begin_events[event_id] |
| 88 | # Create complete event with duration |
| 89 | complete = begin.copy() |
| 90 | complete["dur"] = event.get("ts", 0) - begin.get("ts", 0) |
| 91 | complete["ph"] = "X" # Mark as complete |
| 92 | event = complete # Use this for further processing |
| 93 | del begin_events[event_id] |
| 94 | else: |
| 95 | continue |
| 96 | |
| 97 | # Now process the event (either originally complete or made complete) |
| 98 | if name == "Source": |
| 99 | self.source_events.append(event) |
| 100 | elif name.startswith("Parse"): |
| 101 | self.parse_events.append(event) |
| 102 | elif name.startswith("Instantiate"): |
| 103 | self.instantiate_events.append(event) |
| 104 | elif name in ["Frontend", "Backend", "ExecuteCompiler"]: |
| 105 | self.phase_events[name] = event |
| 106 | |
| 107 | def get_total_time(self) -> float: |
| 108 | """Get total compilation time in milliseconds.""" |
| 109 | exec_event = self.phase_events.get("ExecuteCompiler") |