OLD | NEW |
---|---|
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 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
120 | 120 |
121 def ThreadCategoryName(thread_name): | 121 def ThreadCategoryName(thread_name): |
122 thread_category = "other" | 122 thread_category = "other" |
123 for substring, category in TimelineThreadCategories.iteritems(): | 123 for substring, category in TimelineThreadCategories.iteritems(): |
124 if substring in _MatchBySubString and substring in thread_name: | 124 if substring in _MatchBySubString and substring in thread_name: |
125 thread_category = category | 125 thread_category = category |
126 if thread_name in TimelineThreadCategories: | 126 if thread_name in TimelineThreadCategories: |
127 thread_category = TimelineThreadCategories[thread_name] | 127 thread_category = TimelineThreadCategories[thread_name] |
128 return thread_category | 128 return thread_category |
129 | 129 |
130 def ThreadCpuTimeResultName(thread_category): | 130 def ThreadCpuTimeResultName(thread_category, measure_per_frame=True): |
jdduke (slow)
2015/04/29 18:07:17
I'm not married to the |measure_per_frame| name.
nednguyen
2015/04/30 05:07:45
I don't thing these helpers function add any value
jdduke (slow)
2015/05/01 15:51:44
Done.
| |
131 # This isn't a good name, but I don't want to change it and lose continuity. | 131 # 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" | 132 if measure_per_frame: |
133 return "thread_" + thread_category + "_cpu_time_per_frame" | |
134 return "thread_" + thread_category + "_cpu_time_total" | |
nednguyen
2015/04/30 05:07:45
How stable is this thread_X_cpu_time_total metric?
jdduke (slow)
2015/04/30 14:29:39
In local testing, it's quite stable. Part of that
| |
133 | 135 |
134 def ThreadTasksResultName(thread_category): | 136 def ThreadTasksResultName(thread_category, measure_per_frame=True): |
135 return "tasks_per_frame_" + thread_category | 137 if measure_per_frame: |
138 return "tasks_per_frame_" + thread_category | |
139 return "tasks_" + thread_category | |
nednguyen
2015/04/30 05:07:45
Same here, I think you should come up with a new T
jdduke (slow)
2015/04/30 14:29:39
Will do.
| |
136 | 140 |
137 def ThreadMeanFrameTimeResultName(thread_category): | 141 def ThreadFrameTimeResultName(thread_category, measure_per_frame=True): |
138 return "mean_frame_time_" + thread_category | 142 if measure_per_frame: |
143 return "mean_frame_time_" + thread_category | |
144 return "total_frame_time_" + thread_category | |
139 | 145 |
140 def ThreadDetailResultName(thread_category, detail): | 146 def ThreadDetailResultName(thread_category, detail): |
141 detail_sanitized = detail.replace('.','_') | 147 detail_sanitized = detail.replace('.','_') |
142 return "thread_" + thread_category + "|" + detail_sanitized | 148 return "thread_" + thread_category + "|" + detail_sanitized |
143 | 149 |
144 | 150 |
145 class ResultsForThread(object): | 151 class ResultsForThread(object): |
146 def __init__(self, model, record_ranges, name): | 152 def __init__(self, model, record_ranges, name, measure_per_frame): |
147 self.model = model | 153 self.model = model |
148 self.toplevel_slices = [] | 154 self.toplevel_slices = [] |
149 self.all_slices = [] | 155 self.all_slices = [] |
150 self.name = name | 156 self.name = name |
151 self.record_ranges = record_ranges | 157 self.record_ranges = record_ranges |
152 self.all_action_time = \ | 158 self.all_action_time = \ |
153 sum([record_range.bounds for record_range in self.record_ranges]) | 159 sum([record_range.bounds for record_range in self.record_ranges]) |
160 self.measure_per_frame = measure_per_frame | |
154 | 161 |
155 @property | 162 @property |
156 def clock_time(self): | 163 def clock_time(self): |
157 clock_duration = sum([x.duration for x in self.toplevel_slices]) | 164 clock_duration = sum([x.duration for x in self.toplevel_slices]) |
158 clock_overhead = sum([ClockOverheadForEvent(x) for x in self.all_slices]) | 165 clock_overhead = sum([ClockOverheadForEvent(x) for x in self.all_slices]) |
159 return clock_duration - clock_overhead | 166 return clock_duration - clock_overhead |
160 | 167 |
161 @property | 168 @property |
162 def cpu_time(self): | 169 def cpu_time(self): |
163 cpu_duration = 0 | 170 cpu_duration = 0 |
(...skipping 20 matching lines...) Expand all Loading... | |
184 break | 191 break |
185 return slices_in_actions | 192 return slices_in_actions |
186 | 193 |
187 def AppendThreadSlices(self, thread): | 194 def AppendThreadSlices(self, thread): |
188 self.all_slices.extend(self.SlicesInActions(thread.all_slices)) | 195 self.all_slices.extend(self.SlicesInActions(thread.all_slices)) |
189 self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices)) | 196 self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices)) |
190 | 197 |
191 # Currently we report cpu-time per frame, tasks per frame, and possibly | 198 # Currently we report cpu-time per frame, tasks per frame, and possibly |
192 # the mean frame (if there is a trace specified to find it). | 199 # the mean frame (if there is a trace specified to find it). |
193 def AddResults(self, num_frames, results): | 200 def AddResults(self, num_frames, results): |
194 cpu_per_frame = Rate(self.cpu_time, num_frames) | 201 num_intervals = num_frames if self.measure_per_frame else 1 |
195 tasks_per_frame = Rate(len(self.toplevel_slices), num_frames) | 202 cpu_per_interval = Rate(self.cpu_time, num_intervals) |
203 tasks_per_interval = Rate(len(self.toplevel_slices), num_intervals) | |
196 results.AddValue(scalar.ScalarValue( | 204 results.AddValue(scalar.ScalarValue( |
197 results.current_page, ThreadCpuTimeResultName(self.name), | 205 results.current_page, |
198 'ms', cpu_per_frame)) | 206 ThreadCpuTimeResultName(self.name, self.measure_per_frame), 'ms', |
207 cpu_per_interval)) | |
199 results.AddValue(scalar.ScalarValue( | 208 results.AddValue(scalar.ScalarValue( |
200 results.current_page, ThreadTasksResultName(self.name), | 209 results.current_page, |
201 'tasks', tasks_per_frame)) | 210 ThreadTasksResultName(self.name, self.measure_per_frame), |
211 'tasks', tasks_per_interval)) | |
202 # Report mean frame time if this is the thread we are using for normalizing | 212 # Report mean frame time if this is the thread we are using for normalizing |
203 # other results. We could report other frame rates (eg. renderer_main) but | 213 # other results. We could report other frame rates (eg. renderer_main) but |
204 # this might get confusing. | 214 # this might get confusing. |
205 if self.name == FrameTraceThreadName: | 215 if self.name == FrameTraceThreadName: |
206 num_frames = self.CountTracesWithName(FrameTraceName) | 216 num_intervals = self.CountTracesWithName(FrameTraceName) \ |
207 mean_frame_time = Rate(self.all_action_time, num_frames) | 217 if self.measure_per_frame else 1 |
218 frame_time = Rate(self.all_action_time, num_intervals) | |
208 results.AddValue(scalar.ScalarValue( | 219 results.AddValue(scalar.ScalarValue( |
209 results.current_page, ThreadMeanFrameTimeResultName(self.name), | 220 results.current_page, |
210 'ms', mean_frame_time)) | 221 ThreadFrameTimeResultName(self.name, self.measure_per_frame), |
222 'ms', frame_time)) | |
211 | 223 |
212 def AddDetailedResults(self, num_frames, results): | 224 def AddDetailedResults(self, num_frames, results): |
225 num_intervals = num_frames if self.measure_per_frame else 1 | |
213 slices_by_category = collections.defaultdict(list) | 226 slices_by_category = collections.defaultdict(list) |
214 for s in self.all_slices: | 227 for s in self.all_slices: |
215 slices_by_category[s.category].append(s) | 228 slices_by_category[s.category].append(s) |
216 all_self_times = [] | 229 all_self_times = [] |
217 for category, slices_in_category in slices_by_category.iteritems(): | 230 for category, slices_in_category in slices_by_category.iteritems(): |
218 self_time = sum([x.self_time for x in slices_in_category]) | 231 self_time = sum([x.self_time for x in slices_in_category]) |
219 all_self_times.append(self_time) | 232 all_self_times.append(self_time) |
220 self_time_result = (float(self_time) / num_frames) if num_frames else 0 | 233 self_time_result = ( |
234 (float(self_time) / num_intervals) if num_intervals else 0) | |
221 results.AddValue(scalar.ScalarValue( | 235 results.AddValue(scalar.ScalarValue( |
222 results.current_page, ThreadDetailResultName(self.name, category), | 236 results.current_page, ThreadDetailResultName(self.name, category), |
223 'ms', self_time_result)) | 237 'ms', self_time_result)) |
224 all_measured_time = sum(all_self_times) | 238 all_measured_time = sum(all_self_times) |
225 idle_time = max(0, self.all_action_time - all_measured_time) | 239 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 | 240 idle_time_result = ( |
241 (float(idle_time) / num_intervals) if num_intervals else 0) | |
227 results.AddValue(scalar.ScalarValue( | 242 results.AddValue(scalar.ScalarValue( |
228 results.current_page, ThreadDetailResultName(self.name, "idle"), | 243 results.current_page, ThreadDetailResultName(self.name, "idle"), |
229 'ms', idle_time_result)) | 244 'ms', idle_time_result)) |
230 | 245 |
231 def CountTracesWithName(self, substring): | 246 def CountTracesWithName(self, substring): |
232 count = 0 | 247 count = 0 |
233 for event in self.all_slices: | 248 for event in self.all_slices: |
234 if substring in event.name: | 249 if substring in event.name: |
235 count += 1 | 250 count += 1 |
236 return count | 251 return count |
237 | 252 |
253 | |
238 class ThreadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric): | 254 class ThreadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric): |
239 def __init__(self): | 255 def __init__(self, measure_per_frame=True): |
nednguyen
2015/04/30 05:07:45
Can you refactor this differently so we have two d
jdduke (slow)
2015/04/30 14:29:39
Sure, what about a PerFrameThreadTimesTimelineMetr
| |
240 super(ThreadTimesTimelineMetric, self).__init__() | 256 super(ThreadTimesTimelineMetric, self).__init__() |
241 # Minimal traces, for minimum noise in CPU-time measurements. | 257 # Minimal traces, for minimum noise in CPU-time measurements. |
242 self.results_to_report = AllThreads | 258 self.results_to_report = AllThreads |
243 self.details_to_report = NoThreads | 259 self.details_to_report = NoThreads |
260 self.measure_per_frame = measure_per_frame | |
244 | 261 |
245 def AddResults(self, model, _, interaction_records, results): | 262 def AddResults(self, model, _, interaction_records, results): |
246 # Set up each thread category for consistant results. | 263 # Set up each thread category for consistant results. |
247 thread_category_results = {} | 264 thread_category_results = {} |
248 for name in TimelineThreadCategories.values(): | 265 for name in TimelineThreadCategories.values(): |
249 thread_category_results[name] = ResultsForThread( | 266 thread_category_results[name] = ResultsForThread( |
250 model, [r.GetBounds() for r in interaction_records], name) | 267 model, [r.GetBounds() for r in interaction_records], name, |
268 self.measure_per_frame) | |
251 | 269 |
252 # Group the slices by their thread category. | 270 # Group the slices by their thread category. |
253 for thread in model.GetAllThreads(): | 271 for thread in model.GetAllThreads(): |
254 thread_category = ThreadCategoryName(thread.name) | 272 thread_category = ThreadCategoryName(thread.name) |
255 thread_category_results[thread_category].AppendThreadSlices(thread) | 273 thread_category_results[thread_category].AppendThreadSlices(thread) |
256 | 274 |
257 # Group all threads. | 275 # Group all threads. |
258 for thread in model.GetAllThreads(): | 276 for thread in model.GetAllThreads(): |
259 thread_category_results['total_all'].AppendThreadSlices(thread) | 277 thread_category_results['total_all'].AppendThreadSlices(thread) |
260 | 278 |
261 # Also group fast-path threads. | 279 # Also group fast-path threads. |
262 for thread in model.GetAllThreads(): | 280 for thread in model.GetAllThreads(): |
263 if ThreadCategoryName(thread.name) in FastPathThreads: | 281 if ThreadCategoryName(thread.name) in FastPathThreads: |
264 thread_category_results['total_fast_path'].AppendThreadSlices(thread) | 282 thread_category_results['total_fast_path'].AppendThreadSlices(thread) |
265 | 283 |
266 # Calculate the number of frames. | 284 # Calculate the number of frames. |
267 frame_rate_thread = thread_category_results[FrameTraceThreadName] | 285 frame_rate_thread = thread_category_results[FrameTraceThreadName] |
268 num_frames = frame_rate_thread.CountTracesWithName(FrameTraceName) | 286 num_frames = frame_rate_thread.CountTracesWithName(FrameTraceName) |
269 | 287 |
270 # Report the desired results and details. | 288 # Report the desired results and details. |
271 for thread_results in thread_category_results.values(): | 289 for thread_results in thread_category_results.values(): |
272 if thread_results.name in self.results_to_report: | 290 if thread_results.name in self.results_to_report: |
273 thread_results.AddResults(num_frames, results) | 291 thread_results.AddResults(num_frames, results) |
274 # TOOD(nduca): When generic results objects are done, this special case | 292 # TOOD(nduca): When generic results objects are done, this special case |
275 # can be replaced with a generic UI feature. | 293 # can be replaced with a generic UI feature. |
276 if thread_results.name in self.details_to_report: | 294 if thread_results.name in self.details_to_report: |
277 thread_results.AddDetailedResults(num_frames, results) | 295 thread_results.AddDetailedResults(num_frames, results) |
296 | |
297 def ThreadDetailResultName(self, thread_category, detail): | |
298 return ThreadDetailResultName(thread_category, detail) | |
299 | |
300 def ThreadCpuTimeResultName(self, thread_category): | |
301 return ThreadCpuTimeResultName(thread_category, self.measure_per_frame) | |
302 | |
303 def ThreadTasksResultName(self, thread_category): | |
304 return ThreadTasksResultName(thread_category, self.measure_per_frame) | |
305 | |
306 def ThreadFrameTimeResultName(self, thread_category): | |
307 return ThreadFrameTimeResultName(thread_category, self.measure_per_frame) | |
OLD | NEW |