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

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

Issue 1084533005: Support total and per-second task/threadtime timeline metrics (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Code review (use %) Created 5 years, 7 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
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.util.statistics import DivideIfPossibleOrZero 6 from telemetry.util.statistics import DivideIfPossibleOrZero
7 from telemetry.value import scalar 7 from telemetry.value import scalar
8 from telemetry.web_perf.metrics import timeline_based_metric 8 from telemetry.web_perf.metrics import timeline_based_metric
9 9
10 10
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 ReportMainThreadOnly = ["renderer_main"] 94 ReportMainThreadOnly = ["renderer_main"]
95 ReportSilkDetails = ["renderer_main"] 95 ReportSilkDetails = ["renderer_main"]
96 96
97 # TODO(epenner): Thread names above are likely fairly stable but trace names 97 # TODO(epenner): Thread names above are likely fairly stable but trace names
98 # could change. We should formalize these traces to keep this robust. 98 # could change. We should formalize these traces to keep this robust.
99 OverheadTraceCategory = "trace_event_overhead" 99 OverheadTraceCategory = "trace_event_overhead"
100 OverheadTraceName = "overhead" 100 OverheadTraceName = "overhead"
101 FrameTraceName = "::SwapBuffers" 101 FrameTraceName = "::SwapBuffers"
102 FrameTraceThreadName = "renderer_compositor" 102 FrameTraceThreadName = "renderer_compositor"
103 103
104 IntervalNames = ["frame", "ms"]
105
104 def Rate(numerator, denominator): 106 def Rate(numerator, denominator):
105 return DivideIfPossibleOrZero(numerator, denominator) 107 return DivideIfPossibleOrZero(numerator, denominator)
106 108
107 def ClockOverheadForEvent(event): 109 def ClockOverheadForEvent(event):
108 if (event.category == OverheadTraceCategory and 110 if (event.category == OverheadTraceCategory and
109 event.name == OverheadTraceName): 111 event.name == OverheadTraceName):
110 return event.duration 112 return event.duration
111 else: 113 else:
112 return 0 114 return 0
113 115
114 def CpuOverheadForEvent(event): 116 def CpuOverheadForEvent(event):
115 if (event.category == OverheadTraceCategory and 117 if (event.category == OverheadTraceCategory and
116 event.thread_duration): 118 event.thread_duration):
117 return event.thread_duration 119 return event.thread_duration
118 else: 120 else:
119 return 0 121 return 0
120 122
121 def ThreadCategoryName(thread_name): 123 def ThreadCategoryName(thread_name):
122 thread_category = "other" 124 thread_category = "other"
123 for substring, category in TimelineThreadCategories.iteritems(): 125 for substring, category in TimelineThreadCategories.iteritems():
124 if substring in _MatchBySubString and substring in thread_name: 126 if substring in _MatchBySubString and substring in thread_name:
125 thread_category = category 127 thread_category = category
126 if thread_name in TimelineThreadCategories: 128 if thread_name in TimelineThreadCategories:
127 thread_category = TimelineThreadCategories[thread_name] 129 thread_category = TimelineThreadCategories[thread_name]
128 return thread_category 130 return thread_category
129 131
130 def ThreadCpuTimeResultName(thread_category): 132 def ThreadCpuTimeResultName(thread_category, interval_name):
131 # This isn't a good name, but I don't want to change it and lose continuity. 133 # This isn't a good name, but I don't want to change it and lose continuity.
132 return "thread_" + thread_category + "_cpu_time_per_frame" 134 return "thread_" + thread_category + "_cpu_time_per_" + interval_name
133 135
134 def ThreadTasksResultName(thread_category): 136 def ThreadTasksResultName(thread_category, interval_name):
135 return "tasks_per_frame_" + thread_category 137 return "tasks_per_" + interval_name + "_" + thread_category
136 138
137 def ThreadMeanFrameTimeResultName(thread_category): 139 def ThreadMeanFrameTimeResultName(thread_category):
138 return "mean_frame_time_" + thread_category 140 return "mean_frame_time_" + thread_category
139 141
140 def ThreadDetailResultName(thread_category, detail): 142 def ThreadDetailResultName(thread_category, interval_name, detail):
141 detail_sanitized = detail.replace('.','_') 143 detail_sanitized = detail.replace('.','_')
142 return "thread_" + thread_category + "|" + detail_sanitized 144 interval_sanitized = ""
145 # Special-case per-frame detail names to preserve continuity.
146 if interval_name == "frame":
147 interval_sanitized = ""
148 else:
149 interval_sanitized = "_per_" + interval_name
150 return (
151 "thread_" + thread_category + interval_sanitized + "|" + detail_sanitized)
152
153 def _ReportCpuTimePerIntervalAsPercent(interval_name):
154 # When measuring ms of CPU time per ms of system time, report a %.
155 return interval_name == "ms"
156
157 def ThreadCpuTimeUnits(interval_name):
158 if _ReportCpuTimePerIntervalAsPercent(interval_name):
159 return "%"
160 return "ms"
161
162 def ThreadCpuTimeValue(cpu_time_per_interval, interval_name):
163 if _ReportCpuTimePerIntervalAsPercent(interval_name):
164 return cpu_time_per_interval * 100.0
165 return cpu_time_per_interval
143 166
144 167
145 class ResultsForThread(object): 168 class ResultsForThread(object):
146 def __init__(self, model, record_ranges, name): 169 def __init__(self, model, record_ranges, name):
147 self.model = model 170 self.model = model
148 self.toplevel_slices = [] 171 self.toplevel_slices = []
149 self.all_slices = [] 172 self.all_slices = []
150 self.name = name 173 self.name = name
151 self.record_ranges = record_ranges 174 self.record_ranges = record_ranges
152 self.all_action_time = \ 175 self.all_action_time = \
(...skipping 28 matching lines...) Expand all
181 for record_range in self.record_ranges: 204 for record_range in self.record_ranges:
182 if record_range.ContainsInterval(event.start, event.end): 205 if record_range.ContainsInterval(event.start, event.end):
183 slices_in_actions.append(event) 206 slices_in_actions.append(event)
184 break 207 break
185 return slices_in_actions 208 return slices_in_actions
186 209
187 def AppendThreadSlices(self, thread): 210 def AppendThreadSlices(self, thread):
188 self.all_slices.extend(self.SlicesInActions(thread.all_slices)) 211 self.all_slices.extend(self.SlicesInActions(thread.all_slices))
189 self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices)) 212 self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices))
190 213
191 # Currently we report cpu-time per frame, tasks per frame, and possibly 214 # Reports cpu-time per interval and tasks per interval.
192 # the mean frame (if there is a trace specified to find it). 215 def AddResults(self, num_intervals, interval_name, results):
193 def AddResults(self, num_frames, results): 216 cpu_per_interval = Rate(self.cpu_time, num_intervals)
194 cpu_per_frame = Rate(self.cpu_time, num_frames) 217 tasks_per_interval = Rate(len(self.toplevel_slices), num_intervals)
195 tasks_per_frame = Rate(len(self.toplevel_slices), num_frames)
196 results.AddValue(scalar.ScalarValue( 218 results.AddValue(scalar.ScalarValue(
197 results.current_page, ThreadCpuTimeResultName(self.name), 219 results.current_page,
198 'ms', cpu_per_frame)) 220 ThreadCpuTimeResultName(self.name, interval_name),
221 ThreadCpuTimeUnits(interval_name),
222 ThreadCpuTimeValue(cpu_per_interval, interval_name)))
199 results.AddValue(scalar.ScalarValue( 223 results.AddValue(scalar.ScalarValue(
200 results.current_page, ThreadTasksResultName(self.name), 224 results.current_page,
201 'tasks', tasks_per_frame)) 225 ThreadTasksResultName(self.name, interval_name),
202 # Report mean frame time if this is the thread we are using for normalizing 226 'tasks', tasks_per_interval))
203 # other results. We could report other frame rates (eg. renderer_main) but
204 # this might get confusing.
205 if self.name == FrameTraceThreadName:
206 num_frames = self.CountTracesWithName(FrameTraceName)
207 mean_frame_time = Rate(self.all_action_time, num_frames)
208 results.AddValue(scalar.ScalarValue(
209 results.current_page, ThreadMeanFrameTimeResultName(self.name),
210 'ms', mean_frame_time))
211 227
212 def AddDetailedResults(self, num_frames, results): 228 def AddDetailedResults(self, num_intervals, interval_name, results):
213 slices_by_category = collections.defaultdict(list) 229 slices_by_category = collections.defaultdict(list)
214 for s in self.all_slices: 230 for s in self.all_slices:
215 slices_by_category[s.category].append(s) 231 slices_by_category[s.category].append(s)
216 all_self_times = [] 232 all_self_times = []
217 for category, slices_in_category in slices_by_category.iteritems(): 233 for category, slices_in_category in slices_by_category.iteritems():
218 self_time = sum([x.self_time for x in slices_in_category]) 234 self_time = sum([x.self_time for x in slices_in_category])
219 all_self_times.append(self_time) 235 all_self_times.append(self_time)
220 self_time_result = (float(self_time) / num_frames) if num_frames else 0 236 self_time_per_interval = Rate(self_time, num_intervals)
221 results.AddValue(scalar.ScalarValue( 237 results.AddValue(scalar.ScalarValue(
222 results.current_page, ThreadDetailResultName(self.name, category), 238 results.current_page,
223 'ms', self_time_result)) 239 ThreadDetailResultName(self.name, interval_name, category),
240 ThreadCpuTimeUnits(interval_name),
241 ThreadCpuTimeValue(self_time_per_interval, interval_name)))
224 all_measured_time = sum(all_self_times) 242 all_measured_time = sum(all_self_times)
225 idle_time = max(0, self.all_action_time - all_measured_time) 243 idle_time = max(0, self.all_action_time - all_measured_time)
226 idle_time_result = (float(idle_time) / num_frames) if num_frames else 0 244 idle_time_per_interval = Rate(idle_time, num_intervals)
227 results.AddValue(scalar.ScalarValue( 245 results.AddValue(scalar.ScalarValue(
228 results.current_page, ThreadDetailResultName(self.name, "idle"), 246 results.current_page,
229 'ms', idle_time_result)) 247 ThreadDetailResultName(self.name, interval_name, "idle"),
248 ThreadCpuTimeUnits(interval_name),
249 ThreadCpuTimeValue(idle_time_per_interval, interval_name)))
230 250
231 def CountTracesWithName(self, substring): 251 def CountTracesWithName(self, substring):
232 count = 0 252 count = 0
233 for event in self.all_slices: 253 for event in self.all_slices:
234 if substring in event.name: 254 if substring in event.name:
235 count += 1 255 count += 1
236 return count 256 return count
237 257
258
238 class ThreadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric): 259 class ThreadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric):
239 def __init__(self): 260 def __init__(self):
240 super(ThreadTimesTimelineMetric, self).__init__() 261 super(ThreadTimesTimelineMetric, self).__init__()
241 # Minimal traces, for minimum noise in CPU-time measurements. 262 # Minimal traces, for minimum noise in CPU-time measurements.
242 self.results_to_report = AllThreads 263 self.results_to_report = AllThreads
243 self.details_to_report = NoThreads 264 self.details_to_report = NoThreads
244 265
245 def AddResults(self, model, _, interaction_records, results): 266 def AddResults(self, model, _, interaction_records, results):
246 # Set up each thread category for consistant results. 267 # Set up each thread category for consistant results.
247 thread_category_results = {} 268 thread_category_results = {}
248 for name in TimelineThreadCategories.values(): 269 for name in TimelineThreadCategories.values():
249 thread_category_results[name] = ResultsForThread( 270 thread_category_results[name] = ResultsForThread(
250 model, [r.GetBounds() for r in interaction_records], name) 271 model, [r.GetBounds() for r in interaction_records], name)
251 272
252 # Group the slices by their thread category. 273 # Group the slices by their thread category.
253 for thread in model.GetAllThreads(): 274 for thread in model.GetAllThreads():
254 thread_category = ThreadCategoryName(thread.name) 275 thread_category = ThreadCategoryName(thread.name)
255 thread_category_results[thread_category].AppendThreadSlices(thread) 276 thread_category_results[thread_category].AppendThreadSlices(thread)
256 277
257 # Group all threads. 278 # Group all threads.
258 for thread in model.GetAllThreads(): 279 for thread in model.GetAllThreads():
259 thread_category_results['total_all'].AppendThreadSlices(thread) 280 thread_category_results['total_all'].AppendThreadSlices(thread)
260 281
261 # Also group fast-path threads. 282 # Also group fast-path threads.
262 for thread in model.GetAllThreads(): 283 for thread in model.GetAllThreads():
263 if ThreadCategoryName(thread.name) in FastPathThreads: 284 if ThreadCategoryName(thread.name) in FastPathThreads:
264 thread_category_results['total_fast_path'].AppendThreadSlices(thread) 285 thread_category_results['total_fast_path'].AppendThreadSlices(thread)
265 286
266 # Calculate the number of frames. 287 # Calculate the interaction's number of frames.
267 frame_rate_thread = thread_category_results[FrameTraceThreadName] 288 frame_rate_thread = thread_category_results[FrameTraceThreadName]
268 num_frames = frame_rate_thread.CountTracesWithName(FrameTraceName) 289 num_frames = frame_rate_thread.CountTracesWithName(FrameTraceName)
269 290
270 # Report the desired results and details. 291 # Calculate the interaction's duration.
271 for thread_results in thread_category_results.values(): 292 all_threads = thread_category_results['total_all']
272 if thread_results.name in self.results_to_report: 293 num_ms = all_threads.all_action_time
273 thread_results.AddResults(num_frames, results) 294
274 # TOOD(nduca): When generic results objects are done, this special case 295 # Report the desired results and details for each interval type.
275 # can be replaced with a generic UI feature. 296 intervals = [('frame', num_frames), ('ms', num_ms)]
276 if thread_results.name in self.details_to_report: 297 for (interval_name, num_intervals) in intervals:
277 thread_results.AddDetailedResults(num_frames, results) 298 for thread_results in thread_category_results.values():
299 if thread_results.name in self.results_to_report:
300 thread_results.AddResults(num_intervals, interval_name, results)
301 # TOOD(nduca): When generic results objects are done, this special case
302 # can be replaced with a generic UI feature.
303 if thread_results.name in self.details_to_report:
304 thread_results.AddDetailedResults(
305 num_intervals, interval_name, results)
306
307 # Report mean frame time for the frame rate thread. We could report other
308 # frame rates (eg. renderer_main) but this might get confusing.
309 mean_frame_time = Rate(frame_rate_thread.all_action_time, num_frames)
310 results.AddValue(scalar.ScalarValue(
311 results.current_page,
312 ThreadMeanFrameTimeResultName(FrameTraceThreadName),
313 'ms', mean_frame_time))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698