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

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

Issue 139743018: Telemetry: Remove 'overhead' time from thread times measurement. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@TELEMETRY_fix_trace_import_precision
Patch Set: Rebase. Created 6 years, 10 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 | « tools/perf/measurements/thread_times.py ('k') | tools/perf/metrics/timeline_unittest.py » ('j') | 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 import itertools 5 import itertools
6 6
7 from metrics import Metric 7 from metrics import Metric
8 from telemetry.core.timeline import bounds 8 from telemetry.core.timeline import bounds
9 from telemetry.page import page_measurement 9 from telemetry.page import page_measurement
10 10
11 TRACING_MODE = 'tracing-mode' 11 TRACING_MODE = 'tracing-mode'
12 TIMELINE_MODE = 'timeline-mode' 12 TIMELINE_MODE = 'timeline-mode'
13 13
14
14 class MissingFramesError(page_measurement.MeasurementFailure): 15 class MissingFramesError(page_measurement.MeasurementFailure):
15 def __init__(self): 16 def __init__(self):
16 super(MissingFramesError, self).__init__( 17 super(MissingFramesError, self).__init__(
17 'No frames found in trace. Unable to normalize results.') 18 'No frames found in trace. Unable to normalize results.')
18 19
20
19 class TimelineMetric(Metric): 21 class TimelineMetric(Metric):
20 def __init__(self, mode): 22 def __init__(self, mode):
21 """ Initializes a TimelineMetric object. 23 """ Initializes a TimelineMetric object.
22 """ 24 """
23 super(TimelineMetric, self).__init__() 25 super(TimelineMetric, self).__init__()
24 assert mode in (TRACING_MODE, TIMELINE_MODE) 26 assert mode in (TRACING_MODE, TIMELINE_MODE)
25 self._mode = mode 27 self._mode = mode
26 self._model = None 28 self._model = None
27 self._renderer_process = None 29 self._renderer_process = None
28 self._actions = [] 30 self._actions = []
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 def renderer_process(self): 74 def renderer_process(self):
73 return self._renderer_process 75 return self._renderer_process
74 76
75 @renderer_process.setter 77 @renderer_process.setter
76 def renderer_process(self, p): 78 def renderer_process(self, p):
77 self._renderer_process = p 79 self._renderer_process = p
78 80
79 def AddResults(self, tab, results): 81 def AddResults(self, tab, results):
80 return 82 return
81 83
84
82 class LoadTimesTimelineMetric(TimelineMetric): 85 class LoadTimesTimelineMetric(TimelineMetric):
83 def __init__(self, mode): 86 def __init__(self, mode):
84 super(LoadTimesTimelineMetric, self).__init__(mode) 87 super(LoadTimesTimelineMetric, self).__init__(mode)
85 self.report_main_thread_only = True 88 self.report_main_thread_only = True
86 89
87 def AddResults(self, _, results): 90 def AddResults(self, _, results):
88 assert self._model 91 assert self._model
89 if self.report_main_thread_only: 92 if self.report_main_thread_only:
90 if self._mode == TRACING_MODE: 93 if self._mode == TRACING_MODE:
91 thread_filter = 'CrRendererMain' 94 thread_filter = 'CrRendererMain'
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 136
134 # We want to generate a consistant picture of our thread usage, despite 137 # We want to generate a consistant picture of our thread usage, despite
135 # having several process configurations (in-proc-gpu/single-proc). 138 # having several process configurations (in-proc-gpu/single-proc).
136 # Since we can't isolate renderer threads in single-process mode, we 139 # Since we can't isolate renderer threads in single-process mode, we
137 # always sum renderer-process threads' times. We also sum all io-threads 140 # always sum renderer-process threads' times. We also sum all io-threads
138 # for simplicity. 141 # for simplicity.
139 TimelineThreadCategories = { 142 TimelineThreadCategories = {
140 "Chrome_InProcGpuThread": "GPU", 143 "Chrome_InProcGpuThread": "GPU",
141 "CrGpuMain" : "GPU", 144 "CrGpuMain" : "GPU",
142 "AsyncTransferThread" : "GPU_transfer", 145 "AsyncTransferThread" : "GPU_transfer",
143 "CrBrowserMain" : "browser_main", 146 "CrBrowserMain" : "browser",
144 "Browser Compositor" : "browser_compositor", 147 "Browser Compositor" : "browser",
145 "CrRendererMain" : "renderer_main", 148 "CrRendererMain" : "renderer_main",
146 "Compositor" : "renderer_compositor", 149 "Compositor" : "renderer_compositor",
147 "IOThread" : "IO", 150 "IOThread" : "IO",
148 "CompositorRasterWorker": "raster", 151 "CompositorRasterWorker": "raster",
149 "DummyThreadName1" : "other", 152 "DummyThreadName1" : "other",
150 "DummyThreadName2" : "total_fast_path", 153 "DummyThreadName2" : "total_fast_path",
151 "DummyThreadName3" : "total_all" 154 "DummyThreadName3" : "total_all"
152 } 155 }
153 156
154 MatchBySubString = ["IOThread", "CompositorRasterWorker"] 157 _MatchBySubString = ["IOThread", "CompositorRasterWorker"]
155 158
156 AllThreads = TimelineThreadCategories.values() 159 AllThreads = TimelineThreadCategories.values()
157 NoThreads = [] 160 NoThreads = []
158 FastPathThreads = ["GPU", 161 FastPathThreads = ["GPU", "renderer_compositor", "browser", "IO"]
159 "browser_main", 162
160 "browser_compositor", 163 ReportMainThreadOnly = ["renderer_main"]
161 "renderer_compositor", 164 ReportFastPathResults = AllThreads
162 "IO"] 165 ReportFastPathDetails = NoThreads
163 MainThread = ["renderer_main"] 166 ReportSilkResults = ["renderer_main", "total_all"]
164 FastPathResults = AllThreads 167 ReportSilkDetails = ["renderer_main"]
165 FastPathDetails = NoThreads
166 SilkResults = ["renderer_main", "total_all"]
167 SilkDetails = MainThread
168 168
169 # TODO(epenner): Thread names above are likely fairly stable but trace names 169 # TODO(epenner): Thread names above are likely fairly stable but trace names
170 # could change. We should formalize this trace to keep this robust. 170 # could change. We should formalize these traces to keep this robust.
171 CompositorFrameTraceName = "::SwapBuffers" 171 OverheadTraceCategory = "trace_event_overhead"
172 OverheadTraceName = "overhead"
173 FrameTraceName = "::SwapBuffers"
174 FrameTraceThreadName = "renderer_compositor"
175
176
177 def ClockOverheadForEvent(event):
178 if (event.category == OverheadTraceCategory and
179 event.name == OverheadTraceName):
180 return event.duration
181 else:
182 return 0
183
184 def CpuOverheadForEvent(event):
185 if (event.category == OverheadTraceCategory and
186 event.thread_duration):
187 return event.thread_duration
188 else:
189 return 0
172 190
173 def ThreadCategoryName(thread_name): 191 def ThreadCategoryName(thread_name):
174 thread_category = "other" 192 thread_category = "other"
175 for substring, category in TimelineThreadCategories.iteritems(): 193 for substring, category in TimelineThreadCategories.iteritems():
176 if substring in MatchBySubString and substring in thread_name: 194 if substring in _MatchBySubString and substring in thread_name:
177 thread_category = category 195 thread_category = category
178 if thread_name in TimelineThreadCategories: 196 if thread_name in TimelineThreadCategories:
179 thread_category = TimelineThreadCategories[thread_name] 197 thread_category = TimelineThreadCategories[thread_name]
180 return thread_category 198 return thread_category
181 199
182 def ThreadTimeResultName(thread_category): 200 def ThreadTimeResultName(thread_category):
183 return "thread_" + thread_category + "_clock_time_per_frame" 201 return "thread_" + thread_category + "_clock_time_per_frame"
184 202
185 def ThreadCpuTimeResultName(thread_category): 203 def ThreadCpuTimeResultName(thread_category):
186 return "thread_" + thread_category + "_cpu_time_per_frame" 204 return "thread_" + thread_category + "_cpu_time_per_frame"
187 205
188 def ThreadDetailResultName(thread_category, detail): 206 def ThreadDetailResultName(thread_category, detail):
189 return "thread_" + thread_category + "|" + detail 207 return "thread_" + thread_category + "|" + detail
190 208
209
191 class ResultsForThread(object): 210 class ResultsForThread(object):
192 def __init__(self, model, action_ranges, name): 211 def __init__(self, model, action_ranges, name):
193 self.model = model 212 self.model = model
194 self.toplevel_slices = [] 213 self.toplevel_slices = []
195 self.all_slices = [] 214 self.all_slices = []
196 self.name = name 215 self.name = name
197 self.action_ranges = action_ranges 216 self.action_ranges = action_ranges
198 217
199 @property 218 @property
200 def clock_time(self): 219 def clock_time(self):
201 return sum([x.duration for x in self.toplevel_slices]) 220 clock_duration = sum([x.duration for x in self.toplevel_slices])
221 clock_overhead = sum([ClockOverheadForEvent(x) for x in self.all_slices])
222 return clock_duration - clock_overhead
202 223
203 @property 224 @property
204 def cpu_time(self): 225 def cpu_time(self):
205 res = 0 226 cpu_duration = 0
227 cpu_overhead = sum([CpuOverheadForEvent(x) for x in self.all_slices])
206 for x in self.toplevel_slices: 228 for x in self.toplevel_slices:
207 # Only report thread-duration if we have it for all events. 229 # Only report thread-duration if we have it for all events.
208 # 230 #
209 # A thread_duration of 0 is valid, so this only returns 0 if it is None. 231 # A thread_duration of 0 is valid, so this only returns 0 if it is None.
210 if x.thread_duration == None: 232 if x.thread_duration == None:
211 if not x.duration: 233 if not x.duration:
212 continue 234 continue
213 else: 235 else:
214 return 0 236 return 0
215 else: 237 else:
216 res += x.thread_duration 238 cpu_duration += x.thread_duration
217 return res 239 return cpu_duration - cpu_overhead
218 240
219 def ActionSlices(self, slices): 241 def SlicesInActions(self, slices):
220 slices_in_actions = [] 242 slices_in_actions = []
221 for event in slices: 243 for event in slices:
222 for action_range in self.action_ranges: 244 for action_range in self.action_ranges:
223 if action_range.Contains(bounds.Bounds.CreateFromEvent(event)): 245 if action_range.Contains(bounds.Bounds.CreateFromEvent(event)):
224 slices_in_actions.append(event) 246 slices_in_actions.append(event)
225 break 247 break
226 return slices_in_actions 248 return slices_in_actions
227 249
228 def AppendThreadSlices(self, thread): 250 def AppendThreadSlices(self, thread):
229 self.all_slices.extend(self.ActionSlices(thread.all_slices)) 251 self.all_slices.extend(self.SlicesInActions(thread.all_slices))
230 self.toplevel_slices.extend(self.ActionSlices(thread.toplevel_slices)) 252 self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices))
231 253
232 def AddResults(self, num_frames, results): 254 def AddResults(self, num_frames, results):
233 clock_report_name = ThreadTimeResultName(self.name) 255 clock_report_name = ThreadTimeResultName(self.name)
234 cpu_report_name = ThreadCpuTimeResultName(self.name) 256 cpu_report_name = ThreadCpuTimeResultName(self.name)
235 clock_per_frame = (float(self.clock_time) / num_frames) if num_frames else 0 257 clock_per_frame = (float(self.clock_time) / num_frames) if num_frames else 0
236 cpu_per_frame = (float(self.cpu_time) / num_frames) if num_frames else 0 258 cpu_per_frame = (float(self.cpu_time) / num_frames) if num_frames else 0
237 results.Add(clock_report_name, 'ms', clock_per_frame) 259 results.Add(clock_report_name, 'ms', clock_per_frame)
238 results.Add(cpu_report_name, 'ms', cpu_per_frame) 260 results.Add(cpu_report_name, 'ms', cpu_per_frame)
239 261
240 def AddDetailedResults(self, num_frames, results): 262 def AddDetailedResults(self, num_frames, results):
241 slices_by_category = collections.defaultdict(list) 263 slices_by_category = collections.defaultdict(list)
242 for s in self.all_slices: 264 for s in self.all_slices:
243 slices_by_category[s.category].append(s) 265 slices_by_category[s.category].append(s)
244 all_self_times = [] 266 all_self_times = []
245 for category, slices_in_category in slices_by_category.iteritems(): 267 for category, slices_in_category in slices_by_category.iteritems():
246 self_time = sum([x.self_time for x in slices_in_category]) 268 self_time = sum([x.self_time for x in slices_in_category])
247 all_self_times.append(self_time) 269 all_self_times.append(self_time)
248 self_time_result = (float(self_time) / num_frames) if num_frames else 0 270 self_time_result = (float(self_time) / num_frames) if num_frames else 0
249 results.Add(ThreadDetailResultName(self.name, category), 271 results.Add(ThreadDetailResultName(self.name, category),
250 'ms', self_time_result) 272 'ms', self_time_result)
251 all_measured_time = sum(all_self_times) 273 all_measured_time = sum(all_self_times)
252 all_action_time = sum([action.bounds for action in self.action_ranges]) 274 all_action_time = sum([action.bounds for action in self.action_ranges])
253 idle_time = max(0, all_action_time - all_measured_time) 275 idle_time = max(0, all_action_time - all_measured_time)
254 idle_time_result = (float(idle_time) / num_frames) if num_frames else 0 276 idle_time_result = (float(idle_time) / num_frames) if num_frames else 0
255 results.Add(ThreadDetailResultName(self.name, "idle"), 277 results.Add(ThreadDetailResultName(self.name, "idle"),
256 'ms', idle_time_result) 278 'ms', idle_time_result)
257 279
280
258 class ThreadTimesTimelineMetric(TimelineMetric): 281 class ThreadTimesTimelineMetric(TimelineMetric):
259 def __init__(self): 282 def __init__(self):
260 super(ThreadTimesTimelineMetric, self).__init__(TRACING_MODE) 283 super(ThreadTimesTimelineMetric, self).__init__(TRACING_MODE)
261 self.results_to_report = AllThreads 284 self.results_to_report = AllThreads
262 self.details_to_report = NoThreads 285 self.details_to_report = NoThreads
263 286
264 def CountSlices(self, slices, substring): 287 def CountSlices(self, slices, substring):
265 count = 0 288 count = 0
266 for event in slices: 289 for event in slices:
267 if substring in event.name: 290 if substring in event.name:
(...skipping 18 matching lines...) Expand all
286 309
287 # Group all threads. 310 # Group all threads.
288 for thread in self._model.GetAllThreads(): 311 for thread in self._model.GetAllThreads():
289 thread_category_results['total_all'].AppendThreadSlices(thread) 312 thread_category_results['total_all'].AppendThreadSlices(thread)
290 313
291 # Also group fast-path threads. 314 # Also group fast-path threads.
292 for thread in self._model.GetAllThreads(): 315 for thread in self._model.GetAllThreads():
293 if ThreadCategoryName(thread.name) in FastPathThreads: 316 if ThreadCategoryName(thread.name) in FastPathThreads:
294 thread_category_results['total_fast_path'].AppendThreadSlices(thread) 317 thread_category_results['total_fast_path'].AppendThreadSlices(thread)
295 318
296 # Calculate the number of frames from the CC thread. 319 # Calculate the number of frames.
297 cc_slices = thread_category_results['renderer_compositor'].all_slices 320 frame_slices = thread_category_results[FrameTraceThreadName].all_slices
298 num_frames = self.CountSlices(cc_slices, CompositorFrameTraceName) 321 num_frames = self.CountSlices(frame_slices, FrameTraceName)
299 322
300 # Report the desired results and details. 323 # Report the desired results and details.
301 for thread_results in thread_category_results.values(): 324 for thread_results in thread_category_results.values():
302 if thread_results.name in self.results_to_report: 325 if thread_results.name in self.results_to_report:
303 thread_results.AddResults(num_frames, results) 326 thread_results.AddResults(num_frames, results)
304 # TOOD(nduca): When generic results objects are done, this special case 327 # TOOD(nduca): When generic results objects are done, this special case
305 # can be replaced with a generic UI feature. 328 # can be replaced with a generic UI feature.
306 if thread_results.name in self.details_to_report: 329 if thread_results.name in self.details_to_report:
307 thread_results.AddDetailedResults(num_frames, results) 330 thread_results.AddDetailedResults(num_frames, results)
OLDNEW
« no previous file with comments | « tools/perf/measurements/thread_times.py ('k') | tools/perf/metrics/timeline_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698