Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 import collections | |
| 5 import math | |
| 6 | |
| 7 from telemetry.timeline import async_slice as async_slice_module | |
| 8 from telemetry.timeline import slice as slice_module | |
| 9 from telemetry.value import scalar | |
| 10 from telemetry.web_perf.metrics import timeline_based_metric | |
| 11 | |
| 12 TOPLEVEL_GL_CATEGORY = 'gpu_toplevel' | |
| 13 TOPLEVEL_SERVICE_CATEGORY = 'disabled-by-default-gpu.service' | |
| 14 TOPLEVEL_DEVICE_CATEGORY = 'disabled-by-default-gpu.device' | |
| 15 | |
| 16 FRAME_END_MARKER = ('gpu', 'GLES2DecoderImpl::DoSwapBuffers') | |
|
epenner
2015/01/16 01:17:59
Two things on this trace:
First, is there no GPU-
vmiura
2015/01/16 01:41:15
We should be able to add a "DoSwapBuffers" gpu tra
David Yen
2015/01/16 22:58:04
Done here: https://codereview.chromium.org/7997530
David Yen
2015/01/20 19:37:26
Done.
| |
| 17 | |
| 18 TRACKED_NAMES = { 'RenderCompositor': 'render_compositor', | |
| 19 'Compositor': 'compositor' } | |
| 20 | |
| 21 GPU_SERVICE_DEVICE_VARIANCE = 5 | |
|
epenner
2015/01/16 01:17:59
See below, I'm not sure this threshold is really n
vmiura
2015/01/16 01:41:14
Perhaps if we have the "swap" trace on the GPU we
David Yen
2015/01/16 22:58:04
Yes, once the Swap trace is plumbed through we won
David Yen
2015/01/20 19:37:26
Done.
| |
| 22 | |
| 23 | |
| 24 class GPUTimelineMetric(timeline_based_metric.TimelineBasedMetric): | |
| 25 """Computes GPU based metrics.""" | |
| 26 | |
| 27 def __init__(self): | |
| 28 super(GPUTimelineMetric, self).__init__() | |
| 29 | |
| 30 def AddResults(self, model, _, interaction_records, results): | |
| 31 service_times = self._CalculateGPUTimelineData(model) | |
| 32 for name, durations in service_times.iteritems(): | |
| 33 count = len(durations) | |
| 34 avg = 0.0 | |
| 35 stddev = 0.0 | |
| 36 maximum = 0.0 | |
| 37 if count: | |
| 38 avg = sum(durations) / count | |
| 39 stddev = math.sqrt(sum((d - avg) ** 2 for d in durations) / count) | |
| 40 maximum = max(durations) | |
| 41 | |
| 42 results.AddValue(scalar.ScalarValue(results.current_page, | |
| 43 name + '_max', 'ms', maximum)) | |
| 44 results.AddValue(scalar.ScalarValue(results.current_page, | |
| 45 name + '_avg', 'ms', avg)) | |
| 46 results.AddValue(scalar.ScalarValue(results.current_page, | |
| 47 name + '_stddev', 'ms', stddev)) | |
| 48 | |
| 49 def _CalculateGPUTimelineData(self, model): | |
| 50 """Uses the model and calculates the times for various values for each | |
| 51 frame. The return value will be a dictionary of the following format: | |
| 52 { | |
| 53 EVENT_NAME1: [FRAME0_TIME, FRAME1_TIME...etc.], | |
| 54 EVENT_NAME2: [FRAME0_TIME, FRAME1_TIME...etc.], | |
| 55 } | |
| 56 | |
| 57 Event Names: | |
| 58 total_frame - Total time each frame is calculated to be. | |
| 59 total_gpu_service: Total time the GPU service took per frame. | |
| 60 total_gpu_device: Total time the GPU device took per frame. | |
| 61 TRACKED_NAMES_service: Using the TRACKED_NAMES dictionary, we include | |
|
epenner
2015/01/16 01:17:59
It took me a minute to parse what these mean. Does
vmiura
2015/01/16 01:41:14
Currently the 'gpu.service' traces are using the t
David Yen
2015/01/16 22:58:04
I've changed the Traces to use normal traces inste
David Yen
2015/01/20 19:37:26
Done.
| |
| 62 service traces per frame for the tracked name. | |
| 63 TRACKED_NAMES_device: Using the TRACKED_NAMES dictionary, we include | |
| 64 device traces per frame for the tracked name. | |
| 65 """ | |
| 66 service_events = [] | |
| 67 device_events = [] | |
| 68 buffer_swap_events = [] | |
| 69 | |
| 70 for event in model.IterAllEvents(): | |
| 71 if isinstance(event, slice_module.Slice): | |
| 72 if (event.category, event.name) == FRAME_END_MARKER: | |
| 73 buffer_swap_events.append(event) | |
| 74 elif isinstance(event, async_slice_module.AsyncSlice): | |
| 75 if event.thread_start: | |
|
epenner
2015/01/16 01:17:59
This is the only use of thread_start, are you sure
David Yen
2015/01/16 22:58:04
I was going to look into this later, but the Async
| |
| 76 if event.args.get('gl_category', None) == TOPLEVEL_GL_CATEGORY: | |
| 77 if event.category == TOPLEVEL_SERVICE_CATEGORY: | |
| 78 service_events.append(event) | |
| 79 elif event.category == TOPLEVEL_DEVICE_CATEGORY: | |
| 80 device_events.append(event) | |
| 81 | |
| 82 # Some platforms do not support GPU device tracing, fill in empty values. | |
| 83 no_device_traces = False | |
| 84 if service_events and not device_events: | |
| 85 device_events = [async_slice_module.AsyncSlice(TOPLEVEL_DEVICE_CATEGORY, | |
| 86 event.name, 0) | |
| 87 for event in service_events] | |
| 88 no_device_traces = True | |
| 89 | |
| 90 # Allow some variance in the number of service and device events, depending | |
| 91 # on when the tracing stopped the device trace could not have come back yet. | |
|
epenner
2015/01/16 01:17:59
Another reason for this could be that the device e
David Yen
2015/01/16 22:58:04
This is no longer relevant and will be removed onc
David Yen
2015/01/20 19:37:26
Done.
| |
| 92 if len(service_events) > len(device_events): | |
| 93 event_difference = len(service_events) - len(device_events) | |
| 94 if event_difference <= GPU_SERVICE_DEVICE_VARIANCE: | |
| 95 service_events = service_events[:-event_difference] | |
| 96 | |
| 97 # Group together GPU events and validate that the markers match. | |
| 98 assert len(service_events) == len(device_events), ( | |
|
epenner
2015/01/16 01:17:59
It seems like we should either have a hard '==' in
David Yen
2015/01/16 22:58:04
Before we were using the BufferSwap trace that onl
David Yen
2015/01/20 19:37:26
Done.
| |
| 99 'Mismatching number of GPU Service (%s) and Device events (%s).' % | |
| 100 (len(service_events), len(device_events))) | |
| 101 | |
| 102 service_events_dict = collections.defaultdict(list) | |
| 103 for event in service_events: | |
| 104 service_events_dict[event.name].append(event) | |
| 105 | |
| 106 device_events_dict = collections.defaultdict(list) | |
| 107 for event in device_events: | |
| 108 device_events_dict[event.name].append(event) | |
| 109 | |
| 110 assert set(service_events_dict.keys()) == set(device_events_dict.keys()), ( | |
| 111 'Mismatching event names between GPU Service and Device events.') | |
| 112 | |
| 113 gpu_events = [] | |
| 114 for event_name in service_events_dict: | |
| 115 service_events_list = service_events_dict[event_name] | |
| 116 device_events_list = device_events_dict[event_name] | |
| 117 assert len(service_events_list) == len(device_events_list), ( | |
| 118 'GPU service event (%s) does not correspond with all device events.' % | |
| 119 (event_name)) | |
| 120 | |
| 121 gpu_events.extend(zip(service_events_list, device_events_list)) | |
| 122 | |
| 123 gpu_events.sort(key=lambda events: events[0].start) | |
| 124 | |
| 125 # Utilize Swap Buffer event to separate out gpu events by frames. | |
| 126 gpu_events_by_frame = [] | |
| 127 gpu_event_iter = iter(gpu_events) | |
| 128 current_frame = [] | |
| 129 for buffer_swap_event in buffer_swap_events: | |
|
epenner
2015/01/16 01:17:59
Couple minor things:
Firstly, this looks to be M*
David Yen
2015/01/16 22:58:04
Before I only had SwapBuffer traces on the CPU sid
David Yen
2015/01/20 19:37:26
This is all removed now.
epenner
2015/01/27 22:06:38
I also just noticed that you calculate stats on th
| |
| 130 for gpu_event in gpu_event_iter: | |
| 131 service_event, device_event = gpu_event | |
| 132 if service_event.end <= buffer_swap_event.end: | |
| 133 current_frame.append(gpu_event) | |
| 134 else: | |
| 135 if current_frame: | |
| 136 gpu_events_by_frame.append(current_frame) | |
| 137 current_frame = [gpu_event] | |
| 138 break | |
| 139 | |
| 140 current_frame.extend([gpu_event for gpu_event in gpu_event_iter]) | |
| 141 if current_frame: | |
| 142 gpu_events_by_frame.append(current_frame) | |
| 143 | |
| 144 # Calculate service times that we care about. | |
| 145 total_frame_times = [] | |
| 146 gpu_service_times = [] | |
| 147 gpu_device_times = [] | |
| 148 tracked_times = {} | |
| 149 | |
| 150 tracked_times.update(dict([(value + "_service", []) | |
| 151 for value in TRACKED_NAMES.itervalues()])) | |
| 152 tracked_times.update(dict([(value + "_device", []) | |
| 153 for value in TRACKED_NAMES.itervalues()])) | |
| 154 | |
| 155 if gpu_events: | |
| 156 first_service_event, _ = gpu_events[0] | |
| 157 prev_frame_end = first_service_event.start | |
| 158 else: | |
| 159 prev_frame_end = 0 | |
| 160 | |
| 161 for frame_gpu_events in gpu_events_by_frame: | |
| 162 last_service_in_frame, _ = frame_gpu_events[-1] | |
| 163 | |
| 164 total_frame_time = last_service_in_frame.end - prev_frame_end | |
| 165 prev_frame_end = last_service_in_frame.end | |
| 166 | |
| 167 total_gpu_service_time = 0 | |
| 168 total_gpu_device_time = 0 | |
| 169 tracked_markers = collections.defaultdict(lambda : 0) | |
| 170 for service_event, device_event in frame_gpu_events: | |
| 171 service_time = service_event.end - service_event.start | |
| 172 device_time = device_event.end - device_event.start | |
| 173 total_gpu_service_time += service_time | |
| 174 total_gpu_device_time += device_time | |
| 175 | |
| 176 base_name = service_event.name | |
| 177 dash_index = base_name.rfind('-') | |
| 178 if dash_index != -1: | |
| 179 base_name = base_name[:dash_index] | |
| 180 | |
| 181 tracked_name = TRACKED_NAMES.get(base_name, None) | |
| 182 if tracked_name: | |
| 183 tracked_markers[tracked_name + '_service'] += service_time | |
| 184 tracked_markers[tracked_name + '_device'] += device_time | |
| 185 | |
| 186 total_frame_times.append(total_frame_time) | |
| 187 gpu_service_times.append(total_gpu_service_time) | |
| 188 gpu_device_times.append(total_gpu_device_time) | |
| 189 | |
| 190 for tracked_name in TRACKED_NAMES.values(): | |
| 191 service_name = tracked_name + '_service' | |
| 192 device_name = tracked_name + '_device' | |
| 193 tracked_times[service_name].append(tracked_markers[service_name]) | |
| 194 tracked_times[device_name].append(tracked_markers[device_name]) | |
| 195 | |
| 196 # Create the service times dictionary. | |
| 197 service_times = { 'total_frame': total_frame_times, | |
| 198 'total_gpu_service': gpu_service_times, | |
| 199 'total_gpu_device': gpu_device_times } | |
| 200 service_times.update(tracked_times) | |
| 201 | |
| 202 # Remove device metrics if no device traces were found. | |
| 203 if no_device_traces: | |
| 204 for device_name in [name | |
| 205 for name in service_times.iterkeys() | |
| 206 if name.endswith('_device')]: | |
| 207 service_times.pop(device_name) | |
| 208 | |
| 209 return service_times | |
| OLD | NEW |