Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(397)

Side by Side Diff: tools/perf/metrics/timeline.py

Issue 643343002: Telemetry: Add mean-frame-time and tasks-per-frame (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 import collections 4 import collections
5 5
6 from telemetry.web_perf.metrics import timeline_based_metric 6 from telemetry.web_perf.metrics import timeline_based_metric
7 from telemetry.value import scalar 7 from telemetry.value import scalar
8 8
9 9
10 class LoadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric): 10 class LoadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric):
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 "Browser Compositor" : "browser", 77 "Browser Compositor" : "browser",
78 "CrRendererMain" : "renderer_main", 78 "CrRendererMain" : "renderer_main",
79 "Compositor" : "renderer_compositor", 79 "Compositor" : "renderer_compositor",
80 "IOThread" : "IO", 80 "IOThread" : "IO",
81 "CompositorRasterWorker": "raster", 81 "CompositorRasterWorker": "raster",
82 "DummyThreadName1" : "other", 82 "DummyThreadName1" : "other",
83 "DummyThreadName2" : "total_fast_path", 83 "DummyThreadName2" : "total_fast_path",
84 "DummyThreadName3" : "total_all" 84 "DummyThreadName3" : "total_all"
85 } 85 }
86 86
87 # Normalizing cpu-time by different rates might get confusing, but we could
88 # report different frame-rates by adding their associated trace here.
ernstm 2014/10/11 00:24:22 Why add this if we don't use it? You could instead
epenner 2014/10/13 22:20:32 Ahh, yeah my original goal was to remove those oth
89 ThreadFrameTraceName = {
90 "renderer_compositor": "::SwapBuffers",
91 }
92
87 _MatchBySubString = ["IOThread", "CompositorRasterWorker"] 93 _MatchBySubString = ["IOThread", "CompositorRasterWorker"]
88 94
89 AllThreads = TimelineThreadCategories.values() 95 AllThreads = TimelineThreadCategories.values()
90 NoThreads = [] 96 NoThreads = []
91 FastPathThreads = ["GPU", "renderer_compositor", "browser", "IO"] 97 FastPathThreads = ["GPU", "renderer_compositor", "browser", "IO"]
92 98
93 ReportMainThreadOnly = ["renderer_main"] 99 ReportMainThreadOnly = ["renderer_main"]
94 ReportFastPathResults = AllThreads 100 ReportFastPathResults = AllThreads
95 ReportFastPathDetails = NoThreads 101 ReportFastPathDetails = NoThreads
96 ReportSilkResults = ["renderer_main", "total_all"] 102 ReportSilkResults = ["renderer_main", "total_all"]
97 ReportSilkDetails = ["renderer_main"] 103 ReportSilkDetails = ["renderer_main"]
98 104
99 # TODO(epenner): Thread names above are likely fairly stable but trace names 105 # TODO(epenner): Thread names above are likely fairly stable but trace names
100 # could change. We should formalize these traces to keep this robust. 106 # could change. We should formalize these traces to keep this robust.
101 OverheadTraceCategory = "trace_event_overhead" 107 OverheadTraceCategory = "trace_event_overhead"
102 OverheadTraceName = "overhead" 108 OverheadTraceName = "overhead"
103 FrameTraceName = "::SwapBuffers" 109 FrameTraceName = "::SwapBuffers"
104 FrameTraceThreadName = "renderer_compositor" 110 FrameTraceThreadName = "renderer_compositor"
105 111
112 def Rate(numerator, denominator):
ernstm 2014/10/11 00:24:22 We have telmetry.util.statistics.DivideIfPossibleO
epenner 2014/10/13 22:20:32 That doesn't cast to float. If you think I can cha
ernstm 2014/10/14 17:06:11 Please change DivideIfPossibleOrZero to cast to fl
113 return (float(numerator) / denominator) if denominator else 0
106 114
107 def ClockOverheadForEvent(event): 115 def ClockOverheadForEvent(event):
108 if (event.category == OverheadTraceCategory and 116 if (event.category == OverheadTraceCategory and
109 event.name == OverheadTraceName): 117 event.name == OverheadTraceName):
110 return event.duration 118 return event.duration
111 else: 119 else:
112 return 0 120 return 0
113 121
114 def CpuOverheadForEvent(event): 122 def CpuOverheadForEvent(event):
115 if (event.category == OverheadTraceCategory and 123 if (event.category == OverheadTraceCategory and
116 event.thread_duration): 124 event.thread_duration):
117 return event.thread_duration 125 return event.thread_duration
118 else: 126 else:
119 return 0 127 return 0
120 128
121 def ThreadCategoryName(thread_name): 129 def ThreadCategoryName(thread_name):
122 thread_category = "other" 130 thread_category = "other"
123 for substring, category in TimelineThreadCategories.iteritems(): 131 for substring, category in TimelineThreadCategories.iteritems():
124 if substring in _MatchBySubString and substring in thread_name: 132 if substring in _MatchBySubString and substring in thread_name:
125 thread_category = category 133 thread_category = category
126 if thread_name in TimelineThreadCategories: 134 if thread_name in TimelineThreadCategories:
127 thread_category = TimelineThreadCategories[thread_name] 135 thread_category = TimelineThreadCategories[thread_name]
128 return thread_category 136 return thread_category
129 137
130 def ThreadTimeResultName(thread_category): 138 def ThreadCpuTimeResultName(thread_category):
131 return "thread_" + thread_category + "_clock_time_per_frame" 139 # This name sucks but I don't want to change it and lose continuity
ernstm 2014/10/11 00:24:22 Metrics can be migrated to new names on the dashbo
epenner 2014/10/13 22:20:32 That sounds cool. Got some documentation on that?
ernstm 2014/10/14 17:06:11 qyearsley can help with the data migration. It is
epenner 2014/10/18 01:02:30 Acknowledged.
140 return "thread_" + thread_category + "_cpu_time_per_frame"
132 141
133 def ThreadCpuTimeResultName(thread_category): 142 def ThreadTasksResultName(thread_category):
134 return "thread_" + thread_category + "_cpu_time_per_frame" 143 return "tasks_per_frame_" + thread_category
144
145 def ThreadFpsResultName(thread_category):
146 return "fps_" + thread_category
135 147
136 def ThreadDetailResultName(thread_category, detail): 148 def ThreadDetailResultName(thread_category, detail):
137 detail_sanitized = detail.replace('.','_') 149 detail_sanitized = detail.replace('.','_')
138 return "thread_" + thread_category + "|" + detail_sanitized 150 return "thread_" + thread_category + "|" + detail_sanitized
139 151
140 152
141 class ResultsForThread(object): 153 class ResultsForThread(object):
142 def __init__(self, model, record_ranges, name): 154 def __init__(self, model, record_ranges, name):
143 self.model = model 155 self.model = model
144 self.toplevel_slices = [] 156 self.toplevel_slices = []
145 self.all_slices = [] 157 self.all_slices = []
146 self.name = name 158 self.name = name
147 self.record_ranges = record_ranges 159 self.record_ranges = record_ranges
160 self.all_action_time = \
161 sum([record_range.bounds for record_range in self.record_ranges])
148 162
149 @property 163 @property
150 def clock_time(self): 164 def clock_time(self):
151 clock_duration = sum([x.duration for x in self.toplevel_slices]) 165 clock_duration = sum([x.duration for x in self.toplevel_slices])
152 clock_overhead = sum([ClockOverheadForEvent(x) for x in self.all_slices]) 166 clock_overhead = sum([ClockOverheadForEvent(x) for x in self.all_slices])
153 return clock_duration - clock_overhead 167 return clock_duration - clock_overhead
154 168
155 @property 169 @property
156 def cpu_time(self): 170 def cpu_time(self):
157 cpu_duration = 0 171 cpu_duration = 0
(...skipping 17 matching lines...) Expand all
175 for record_range in self.record_ranges: 189 for record_range in self.record_ranges:
176 if record_range.ContainsInterval(event.start, event.end): 190 if record_range.ContainsInterval(event.start, event.end):
177 slices_in_actions.append(event) 191 slices_in_actions.append(event)
178 break 192 break
179 return slices_in_actions 193 return slices_in_actions
180 194
181 def AppendThreadSlices(self, thread): 195 def AppendThreadSlices(self, thread):
182 self.all_slices.extend(self.SlicesInActions(thread.all_slices)) 196 self.all_slices.extend(self.SlicesInActions(thread.all_slices))
183 self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices)) 197 self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices))
184 198
199 # Currently we report cpu-time per frame, tasks per frame, and possibly
200 # the frame rate (if there is a trace specified to find it).
185 def AddResults(self, num_frames, results): 201 def AddResults(self, num_frames, results):
186 cpu_per_frame = (float(self.cpu_time) / num_frames) if num_frames else 0 202 cpu_per_frame = Rate(self.cpu_time, num_frames)
203 tasks_per_frame = Rate(len(self.toplevel_slices), num_frames)
187 results.AddValue(scalar.ScalarValue( 204 results.AddValue(scalar.ScalarValue(
188 results.current_page, ThreadCpuTimeResultName(self.name), 205 results.current_page, ThreadCpuTimeResultName(self.name),
189 'ms', cpu_per_frame)) 206 'ms', cpu_per_frame))
207 results.AddValue(scalar.ScalarValue(
208 results.current_page, ThreadTasksResultName(self.name),
209 'tasks', tasks_per_frame))
210 if self.name in ThreadFrameTraceName:
211 num_frames = self.CountTracesWithName(ThreadFrameTraceName[self.name])
212 frame_rate = Rate(num_frames, self.all_action_time) * 1000
213 results.AddValue(scalar.ScalarValue(
214 results.current_page, ThreadFpsResultName(self.name),
215 'fps', frame_rate))
190 216
191 def AddDetailedResults(self, num_frames, results): 217 def AddDetailedResults(self, num_frames, results):
192 slices_by_category = collections.defaultdict(list) 218 slices_by_category = collections.defaultdict(list)
193 for s in self.all_slices: 219 for s in self.all_slices:
194 slices_by_category[s.category].append(s) 220 slices_by_category[s.category].append(s)
195 all_self_times = [] 221 all_self_times = []
196 for category, slices_in_category in slices_by_category.iteritems(): 222 for category, slices_in_category in slices_by_category.iteritems():
197 self_time = sum([x.self_time for x in slices_in_category]) 223 self_time = sum([x.self_time for x in slices_in_category])
198 all_self_times.append(self_time) 224 all_self_times.append(self_time)
199 self_time_result = (float(self_time) / num_frames) if num_frames else 0 225 self_time_result = (float(self_time) / num_frames) if num_frames else 0
200 results.AddValue(scalar.ScalarValue( 226 results.AddValue(scalar.ScalarValue(
201 results.current_page, ThreadDetailResultName(self.name, category), 227 results.current_page, ThreadDetailResultName(self.name, category),
202 'ms', self_time_result)) 228 'ms', self_time_result))
203 all_measured_time = sum(all_self_times) 229 all_measured_time = sum(all_self_times)
204 all_action_time = \ 230 idle_time = max(0, self.all_action_time - all_measured_time)
205 sum([record_range.bounds for record_range in self.record_ranges])
206 idle_time = max(0, all_action_time - all_measured_time)
207 idle_time_result = (float(idle_time) / num_frames) if num_frames else 0 231 idle_time_result = (float(idle_time) / num_frames) if num_frames else 0
208 results.AddValue(scalar.ScalarValue( 232 results.AddValue(scalar.ScalarValue(
209 results.current_page, ThreadDetailResultName(self.name, "idle"), 233 results.current_page, ThreadDetailResultName(self.name, "idle"),
210 'ms', idle_time_result)) 234 'ms', idle_time_result))
211 235
236 def CountTracesWithName(self, substring):
237 count = 0
238 for event in self.all_slices:
239 if substring in event.name:
240 count += 1
241 return count
212 242
213 class ThreadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric): 243 class ThreadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric):
214 def __init__(self): 244 def __init__(self):
215 super(ThreadTimesTimelineMetric, self).__init__() 245 super(ThreadTimesTimelineMetric, self).__init__()
216 # Minimal traces, for minimum noise in CPU-time measurements. 246 # Minimal traces, for minimum noise in CPU-time measurements.
217 self.results_to_report = AllThreads 247 self.results_to_report = AllThreads
218 self.details_to_report = NoThreads 248 self.details_to_report = NoThreads
219 249
220 def CountSlices(self, slices, substring):
221 count = 0
222 for event in slices:
223 if substring in event.name:
224 count += 1
225 return count
226
227 def AddResults(self, model, _, interaction_records, results): 250 def AddResults(self, model, _, interaction_records, results):
228 # Set up each thread category for consistant results. 251 # Set up each thread category for consistant results.
229 thread_category_results = {} 252 thread_category_results = {}
230 for name in TimelineThreadCategories.values(): 253 for name in TimelineThreadCategories.values():
231 thread_category_results[name] = ResultsForThread( 254 thread_category_results[name] = ResultsForThread(
232 model, [r.GetBounds() for r in interaction_records], name) 255 model, [r.GetBounds() for r in interaction_records], name)
233 256
234 # Group the slices by their thread category. 257 # Group the slices by their thread category.
235 for thread in model.GetAllThreads(): 258 for thread in model.GetAllThreads():
236 thread_category = ThreadCategoryName(thread.name) 259 thread_category = ThreadCategoryName(thread.name)
237 thread_category_results[thread_category].AppendThreadSlices(thread) 260 thread_category_results[thread_category].AppendThreadSlices(thread)
238 261
239 # Group all threads. 262 # Group all threads.
240 for thread in model.GetAllThreads(): 263 for thread in model.GetAllThreads():
241 thread_category_results['total_all'].AppendThreadSlices(thread) 264 thread_category_results['total_all'].AppendThreadSlices(thread)
242 265
243 # Also group fast-path threads. 266 # Also group fast-path threads.
244 for thread in model.GetAllThreads(): 267 for thread in model.GetAllThreads():
245 if ThreadCategoryName(thread.name) in FastPathThreads: 268 if ThreadCategoryName(thread.name) in FastPathThreads:
246 thread_category_results['total_fast_path'].AppendThreadSlices(thread) 269 thread_category_results['total_fast_path'].AppendThreadSlices(thread)
247 270
248 # Calculate the number of frames. 271 # Calculate the number of frames / frame-rate.
249 frame_slices = thread_category_results[FrameTraceThreadName].all_slices 272 frame_rate_thread = thread_category_results[FrameTraceThreadName]
250 num_frames = self.CountSlices(frame_slices, FrameTraceName) 273 num_frames = frame_rate_thread.CountTracesWithName(FrameTraceName)
251 274
252 # Report the desired results and details. 275 # Report the desired results and details.
253 for thread_results in thread_category_results.values(): 276 for thread_results in thread_category_results.values():
254 if thread_results.name in self.results_to_report: 277 if thread_results.name in self.results_to_report:
255 thread_results.AddResults(num_frames, results) 278 thread_results.AddResults(num_frames, results)
256 # TOOD(nduca): When generic results objects are done, this special case 279 # TOOD(nduca): When generic results objects are done, this special case
257 # can be replaced with a generic UI feature. 280 # can be replaced with a generic UI feature.
258 if thread_results.name in self.details_to_report: 281 if thread_results.name in self.details_to_report:
259 thread_results.AddDetailedResults(num_frames, results) 282 thread_results.AddDetailedResults(num_frames, results)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698