diff --git a/lglpy/timeline/data/raw_trace.py b/lglpy/timeline/data/raw_trace.py index 9ceb379..da0cd83 100644 --- a/lglpy/timeline/data/raw_trace.py +++ b/lglpy/timeline/data/raw_trace.py @@ -878,19 +878,22 @@ def load_perfetto_from_file( # Sort events into time order trace_events.sort(key=lambda x: x.start_time) - # Replace time so first event starts at time = 0 + # Replace time so first event starts at time = 0 and that queued time + # waiting for earlier work does not show as running streams = {} start_time = trace_events[0].start_time for event in trace_events: event.start_time -= start_time - # Also ensure that queued time does not show as running + # First job in stream so just add to stream if event.stream not in streams: streams[event.stream] = [event] continue + # Later job in stream so remove any overlap with job N-1 last_event = streams[event.stream][-1] last_event_end = last_event.start_time + last_event.duration + streams[event.stream].append(event) # Remove overlap if queued while last event still running if event.start_time <= last_event_end: diff --git a/lglpy/timeline/gui/timeline/info_widget.py b/lglpy/timeline/gui/timeline/info_widget.py index 4400998..c462780 100644 --- a/lglpy/timeline/gui/timeline/info_widget.py +++ b/lglpy/timeline/gui/timeline/info_widget.py @@ -28,7 +28,7 @@ from collections import defaultdict -from ...data.raw_trace import GPUStreamID +from ...data.raw_trace import GPUStreamID, GPUStageID from ...data.processed_trace import GPUWorkload from ...drawable.text_pane_widget import TextPaneWidget @@ -251,13 +251,71 @@ def get_active_region_stats(self): return self.cached_range_info + def compute_active_event_stats_single(self, event): + ''' + Get the metrics for the active event. + + This function uses a cached lookup to avoid re-calculating every + redraw, as the stats computation can be quite slow. + + Args: + event: The active event. + + Returns: + List of lines to be printed. + ''' + # Skip non-workload types + if not isinstance(event, GPUWorkload): + return [] + + stream_name = GPUStreamID.get_ui_name(event.stream) + stage_name = GPUStageID.get_ui_name(event.stage) + + metrics = [''] + + # Report total runtime of the selected workloads + other_names = [ + 'API workloads:', + 'Hardware workloads:' + ] + + metrics.append('Active workload runtime:') + label_len = len(stream_name) + len(' stream:') + label_len = max(max(len(x) for x in other_names), label_len) + + label = other_names[0] + metrics.append(f' {label:{label_len}} {1:>5}') + + label = other_names[1] + metrics.append(f' {label:{label_len}} {1:>5}') + + label = f'{stream_name} stream:' + duration = float(event.duration) / 1000000.0 + metrics.append(f' {label:{label_len}} {duration:>5.2f} ms') + + # Report total N workloads + metrics.append('') + metrics.append('Workload properties:') + + label = event.get_workload_name() + metrics.append(f' Name: {label}') + metrics.append(f' Stream: {stream_name}') + metrics.append(f' Stage: {stage_name}') + metrics.append(f' Start: {event.start_time / 1000000.0:0.2f} ms') + metrics.append(f' Duration: {event.duration / 1000000.0:0.2f} ms') + metrics.append('') + return metrics + def compute_active_event_stats_multi(self, active): ''' - Get the metrics for the active time range. + Get the metrics for the active events. This function uses a cached lookup to avoid re-calculating every redraw, as the stats computation can be quite slow. + Args: + active: List of active events. + Returns: List of lines to be printed. ''' @@ -368,6 +426,9 @@ def get_active_event_stats(self): if len(active) == 0: info = None + elif len(active) == 1: + info = self.compute_active_event_stats_single(active[0]) + else: info = self.compute_active_event_stats_multi(active)