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 | 4 |
5 import logging | 5 import logging |
6 | 6 |
7 from telemetry.util import perf_tests_helper | 7 from telemetry.util import perf_tests_helper |
8 from telemetry.util import statistics | 8 from telemetry.util import statistics |
| 9 from telemetry.value import improvement_direction |
9 from telemetry.value import list_of_scalar_values | 10 from telemetry.value import list_of_scalar_values |
10 from telemetry.value import scalar | 11 from telemetry.value import scalar |
11 from telemetry.web_perf.metrics import rendering_stats | 12 from telemetry.web_perf.metrics import rendering_stats |
12 from telemetry.web_perf.metrics import timeline_based_metric | 13 from telemetry.web_perf.metrics import timeline_based_metric |
13 | 14 |
14 | 15 |
15 NOT_ENOUGH_FRAMES_MESSAGE = ( | 16 NOT_ENOUGH_FRAMES_MESSAGE = ( |
16 'Not enough frames for smoothness metrics (at least two are required).\n' | 17 'Not enough frames for smoothness metrics (at least two are required).\n' |
17 'Issues that have caused this in the past:\n' | 18 'Issues that have caused this in the past:\n' |
18 '- Browser bugs that prevents the page from redrawing\n' | 19 '- Browser bugs that prevents the page from redrawing\n' |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 max_frame_delay = round(max(normalized_frame_lengths)) | 135 max_frame_delay = round(max(normalized_frame_lengths)) |
135 frame_lengths = normalized_frame_lengths | 136 frame_lengths = normalized_frame_lengths |
136 else: | 137 else: |
137 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 138 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
138 | 139 |
139 return ( | 140 return ( |
140 scalar.ScalarValue( | 141 scalar.ScalarValue( |
141 page, 'avg_surface_fps', 'fps', avg_surface_fps, | 142 page, 'avg_surface_fps', 'fps', avg_surface_fps, |
142 description='Average frames per second as measured by the ' | 143 description='Average frames per second as measured by the ' |
143 'platform\'s SurfaceFlinger.', | 144 'platform\'s SurfaceFlinger.', |
144 none_value_reason=none_value_reason), | 145 none_value_reason=none_value_reason, |
| 146 improvement_direction=improvement_direction.UP), |
145 scalar.ScalarValue( | 147 scalar.ScalarValue( |
146 page, 'jank_count', 'janks', jank_count, | 148 page, 'jank_count', 'janks', jank_count, |
147 description='Number of changes in frame rate as measured by the ' | 149 description='Number of changes in frame rate as measured by the ' |
148 'platform\'s SurfaceFlinger.', | 150 'platform\'s SurfaceFlinger.', |
149 none_value_reason=none_value_reason), | 151 none_value_reason=none_value_reason, |
| 152 improvement_direction=improvement_direction.DOWN), |
150 scalar.ScalarValue( | 153 scalar.ScalarValue( |
151 page, 'max_frame_delay', 'vsyncs', max_frame_delay, | 154 page, 'max_frame_delay', 'vsyncs', max_frame_delay, |
152 description='Largest frame time as measured by the platform\'s ' | 155 description='Largest frame time as measured by the platform\'s ' |
153 'SurfaceFlinger.', | 156 'SurfaceFlinger.', |
154 none_value_reason=none_value_reason), | 157 none_value_reason=none_value_reason, |
| 158 improvement_direction=improvement_direction.DOWN), |
155 list_of_scalar_values.ListOfScalarValues( | 159 list_of_scalar_values.ListOfScalarValues( |
156 page, 'frame_lengths', 'vsyncs', frame_lengths, | 160 page, 'frame_lengths', 'vsyncs', frame_lengths, |
157 description='Frame time in vsyncs as measured by the platform\'s ' | 161 description='Frame time in vsyncs as measured by the platform\'s ' |
158 'SurfaceFlinger.', | 162 'SurfaceFlinger.', |
159 none_value_reason=none_value_reason) | 163 none_value_reason=none_value_reason, |
| 164 improvement_direction=improvement_direction.DOWN) |
160 ) | 165 ) |
161 | 166 |
162 def _ComputeLatencyMetric(self, page, stats, name, list_of_latency_lists): | 167 def _ComputeLatencyMetric(self, page, stats, name, list_of_latency_lists): |
163 """Returns Values for the mean and discrepancy for given latency stats.""" | 168 """Returns Values for the mean and discrepancy for given latency stats.""" |
164 mean_latency = None | 169 mean_latency = None |
165 latency_discrepancy = None | 170 latency_discrepancy = None |
166 none_value_reason = None | 171 none_value_reason = None |
167 if self._HasEnoughFrames(stats.frame_timestamps): | 172 if self._HasEnoughFrames(stats.frame_timestamps): |
168 latency_list = perf_tests_helper.FlattenList(list_of_latency_lists) | 173 latency_list = perf_tests_helper.FlattenList(list_of_latency_lists) |
169 if len(latency_list) == 0: | 174 if len(latency_list) == 0: |
170 return () | 175 return () |
171 mean_latency = round(statistics.ArithmeticMean(latency_list), 3) | 176 mean_latency = round(statistics.ArithmeticMean(latency_list), 3) |
172 latency_discrepancy = ( | 177 latency_discrepancy = ( |
173 round(statistics.DurationsDiscrepancy(latency_list), 4)) | 178 round(statistics.DurationsDiscrepancy(latency_list), 4)) |
174 else: | 179 else: |
175 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 180 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
176 return ( | 181 return ( |
177 scalar.ScalarValue( | 182 scalar.ScalarValue( |
178 page, 'mean_%s' % name, 'ms', mean_latency, | 183 page, 'mean_%s' % name, 'ms', mean_latency, |
179 description='Arithmetic mean of the raw %s values' % name, | 184 description='Arithmetic mean of the raw %s values' % name, |
180 none_value_reason=none_value_reason), | 185 none_value_reason=none_value_reason, |
| 186 improvement_direction=improvement_direction.DOWN), |
181 scalar.ScalarValue( | 187 scalar.ScalarValue( |
182 page, '%s_discrepancy' % name, 'ms', latency_discrepancy, | 188 page, '%s_discrepancy' % name, 'ms', latency_discrepancy, |
183 description='Discrepancy of the raw %s values' % name, | 189 description='Discrepancy of the raw %s values' % name, |
184 none_value_reason=none_value_reason) | 190 none_value_reason=none_value_reason, |
| 191 improvement_direction=improvement_direction.DOWN) |
185 ) | 192 ) |
186 | 193 |
187 def _ComputeFirstGestureScrollUpdateLatencies(self, page, stats): | 194 def _ComputeFirstGestureScrollUpdateLatencies(self, page, stats): |
188 """Returns a ListOfScalarValuesValues of gesture scroll update latencies. | 195 """Returns a ListOfScalarValuesValues of gesture scroll update latencies. |
189 | 196 |
190 Returns a Value for the first gesture scroll update latency for each | 197 Returns a Value for the first gesture scroll update latency for each |
191 interaction record in |stats|. | 198 interaction record in |stats|. |
192 """ | 199 """ |
193 none_value_reason = None | 200 none_value_reason = None |
194 first_gesture_scroll_update_latencies = [round(latencies[0], 4) | 201 first_gesture_scroll_update_latencies = [round(latencies[0], 4) |
195 for latencies in stats.gesture_scroll_update_latency | 202 for latencies in stats.gesture_scroll_update_latency |
196 if len(latencies)] | 203 if len(latencies)] |
197 if (not self._HasEnoughFrames(stats.frame_timestamps) or | 204 if (not self._HasEnoughFrames(stats.frame_timestamps) or |
198 not first_gesture_scroll_update_latencies): | 205 not first_gesture_scroll_update_latencies): |
199 first_gesture_scroll_update_latencies = None | 206 first_gesture_scroll_update_latencies = None |
200 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 207 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
201 return list_of_scalar_values.ListOfScalarValues( | 208 return list_of_scalar_values.ListOfScalarValues( |
202 page, 'first_gesture_scroll_update_latency', 'ms', | 209 page, 'first_gesture_scroll_update_latency', 'ms', |
203 first_gesture_scroll_update_latencies, | 210 first_gesture_scroll_update_latencies, |
204 description='First gesture scroll update latency measures the time it ' | 211 description='First gesture scroll update latency measures the time it ' |
205 'takes to process the very first gesture scroll update ' | 212 'takes to process the very first gesture scroll update ' |
206 'input event. The first scroll gesture can often get ' | 213 'input event. The first scroll gesture can often get ' |
207 'delayed by work related to page loading.', | 214 'delayed by work related to page loading.', |
208 none_value_reason=none_value_reason) | 215 none_value_reason=none_value_reason, |
| 216 improvement_direction=improvement_direction.DOWN) |
209 | 217 |
210 def _ComputeQueueingDuration(self, page, stats): | 218 def _ComputeQueueingDuration(self, page, stats): |
211 """Returns a Value for the frame queueing durations.""" | 219 """Returns a Value for the frame queueing durations.""" |
212 queueing_durations = None | 220 queueing_durations = None |
213 none_value_reason = None | 221 none_value_reason = None |
214 if 'frame_queueing_durations' in stats.errors: | 222 if 'frame_queueing_durations' in stats.errors: |
215 none_value_reason = stats.errors['frame_queueing_durations'] | 223 none_value_reason = stats.errors['frame_queueing_durations'] |
216 elif self._HasEnoughFrames(stats.frame_timestamps): | 224 elif self._HasEnoughFrames(stats.frame_timestamps): |
217 queueing_durations = perf_tests_helper.FlattenList( | 225 queueing_durations = perf_tests_helper.FlattenList( |
218 stats.frame_queueing_durations) | 226 stats.frame_queueing_durations) |
219 if len(queueing_durations) == 0: | 227 if len(queueing_durations) == 0: |
220 queueing_durations = None | 228 queueing_durations = None |
221 none_value_reason = 'No frame queueing durations recorded.' | 229 none_value_reason = 'No frame queueing durations recorded.' |
222 else: | 230 else: |
223 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 231 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
224 return list_of_scalar_values.ListOfScalarValues( | 232 return list_of_scalar_values.ListOfScalarValues( |
225 page, 'queueing_durations', 'ms', queueing_durations, | 233 page, 'queueing_durations', 'ms', queueing_durations, |
226 description='The frame queueing duration quantifies how out of sync ' | 234 description='The frame queueing duration quantifies how out of sync ' |
227 'the compositor and renderer threads are. It is the amount ' | 235 'the compositor and renderer threads are. It is the amount ' |
228 'of wall time that elapses between a ' | 236 'of wall time that elapses between a ' |
229 'ScheduledActionSendBeginMainFrame event in the compositor ' | 237 'ScheduledActionSendBeginMainFrame event in the compositor ' |
230 'thread and the corresponding BeginMainFrame event in the ' | 238 'thread and the corresponding BeginMainFrame event in the ' |
231 'main thread.', | 239 'main thread.', |
232 none_value_reason=none_value_reason) | 240 none_value_reason=none_value_reason, |
| 241 improvement_direction=improvement_direction.DOWN) |
233 | 242 |
234 def _ComputeFrameTimeMetric(self, page, stats): | 243 def _ComputeFrameTimeMetric(self, page, stats): |
235 """Returns Values for the frame time metrics. | 244 """Returns Values for the frame time metrics. |
236 | 245 |
237 This includes the raw and mean frame times, as well as the percentage of | 246 This includes the raw and mean frame times, as well as the percentage of |
238 frames that were hitting 60 fps. | 247 frames that were hitting 60 fps. |
239 """ | 248 """ |
240 frame_times = None | 249 frame_times = None |
241 mean_frame_time = None | 250 mean_frame_time = None |
242 percentage_smooth = None | 251 percentage_smooth = None |
243 none_value_reason = None | 252 none_value_reason = None |
244 if self._HasEnoughFrames(stats.frame_timestamps): | 253 if self._HasEnoughFrames(stats.frame_timestamps): |
245 frame_times = perf_tests_helper.FlattenList(stats.frame_times) | 254 frame_times = perf_tests_helper.FlattenList(stats.frame_times) |
246 mean_frame_time = round(statistics.ArithmeticMean(frame_times), 3) | 255 mean_frame_time = round(statistics.ArithmeticMean(frame_times), 3) |
247 # We use 17ms as a somewhat looser threshold, instead of 1000.0/60.0. | 256 # We use 17ms as a somewhat looser threshold, instead of 1000.0/60.0. |
248 smooth_threshold = 17.0 | 257 smooth_threshold = 17.0 |
249 smooth_count = sum(1 for t in frame_times if t < smooth_threshold) | 258 smooth_count = sum(1 for t in frame_times if t < smooth_threshold) |
250 percentage_smooth = float(smooth_count) / len(frame_times) * 100.0 | 259 percentage_smooth = float(smooth_count) / len(frame_times) * 100.0 |
251 else: | 260 else: |
252 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 261 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
253 return ( | 262 return ( |
254 list_of_scalar_values.ListOfScalarValues( | 263 list_of_scalar_values.ListOfScalarValues( |
255 page, 'frame_times', 'ms', frame_times, | 264 page, 'frame_times', 'ms', frame_times, |
256 description='List of raw frame times, helpful to understand the ' | 265 description='List of raw frame times, helpful to understand the ' |
257 'other metrics.', | 266 'other metrics.', |
258 none_value_reason=none_value_reason), | 267 none_value_reason=none_value_reason, |
| 268 improvement_direction=improvement_direction.DOWN), |
259 scalar.ScalarValue( | 269 scalar.ScalarValue( |
260 page, 'mean_frame_time', 'ms', mean_frame_time, | 270 page, 'mean_frame_time', 'ms', mean_frame_time, |
261 description='Arithmetic mean of frame times.', | 271 description='Arithmetic mean of frame times.', |
262 none_value_reason=none_value_reason), | 272 none_value_reason=none_value_reason, |
| 273 improvement_direction=improvement_direction.DOWN), |
263 scalar.ScalarValue( | 274 scalar.ScalarValue( |
264 page, 'percentage_smooth', 'score', percentage_smooth, | 275 page, 'percentage_smooth', 'score', percentage_smooth, |
265 description='Percentage of frames that were hitting 60 fps.', | 276 description='Percentage of frames that were hitting 60 fps.', |
266 none_value_reason=none_value_reason) | 277 none_value_reason=none_value_reason, |
| 278 improvement_direction=improvement_direction.DOWN) |
267 ) | 279 ) |
268 | 280 |
269 def _ComputeFrameTimeDiscrepancy(self, page, stats): | 281 def _ComputeFrameTimeDiscrepancy(self, page, stats): |
270 """Returns a Value for the absolute discrepancy of frame time stamps.""" | 282 """Returns a Value for the absolute discrepancy of frame time stamps.""" |
271 | 283 |
272 frame_discrepancy = None | 284 frame_discrepancy = None |
273 none_value_reason = None | 285 none_value_reason = None |
274 if self._HasEnoughFrames(stats.frame_timestamps): | 286 if self._HasEnoughFrames(stats.frame_timestamps): |
275 frame_discrepancy = round(statistics.TimestampsDiscrepancy( | 287 frame_discrepancy = round(statistics.TimestampsDiscrepancy( |
276 stats.frame_timestamps), 4) | 288 stats.frame_timestamps), 4) |
277 else: | 289 else: |
278 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 290 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
279 return scalar.ScalarValue( | 291 return scalar.ScalarValue( |
280 page, 'frame_time_discrepancy', 'ms', frame_discrepancy, | 292 page, 'frame_time_discrepancy', 'ms', frame_discrepancy, |
281 description='Absolute discrepancy of frame time stamps, where ' | 293 description='Absolute discrepancy of frame time stamps, where ' |
282 'discrepancy is a measure of irregularity. It quantifies ' | 294 'discrepancy is a measure of irregularity. It quantifies ' |
283 'the worst jank. For a single pause, discrepancy ' | 295 'the worst jank. For a single pause, discrepancy ' |
284 'corresponds to the length of this pause in milliseconds. ' | 296 'corresponds to the length of this pause in milliseconds. ' |
285 'Consecutive pauses increase the discrepancy. This metric ' | 297 'Consecutive pauses increase the discrepancy. This metric ' |
286 'is important because even if the mean and 95th ' | 298 'is important because even if the mean and 95th ' |
287 'percentile are good, one long pause in the middle of an ' | 299 'percentile are good, one long pause in the middle of an ' |
288 'interaction is still bad.', | 300 'interaction is still bad.', |
289 none_value_reason=none_value_reason) | 301 none_value_reason=none_value_reason, |
| 302 improvement_direction=improvement_direction.DOWN) |
290 | 303 |
291 def _ComputeMeanPixelsApproximated(self, page, stats): | 304 def _ComputeMeanPixelsApproximated(self, page, stats): |
292 """Add the mean percentage of pixels approximated. | 305 """Add the mean percentage of pixels approximated. |
293 | 306 |
294 This looks at tiles which are missing or of low or non-ideal resolution. | 307 This looks at tiles which are missing or of low or non-ideal resolution. |
295 """ | 308 """ |
296 mean_pixels_approximated = None | 309 mean_pixels_approximated = None |
297 none_value_reason = None | 310 none_value_reason = None |
298 if self._HasEnoughFrames(stats.frame_timestamps): | 311 if self._HasEnoughFrames(stats.frame_timestamps): |
299 mean_pixels_approximated = round(statistics.ArithmeticMean( | 312 mean_pixels_approximated = round(statistics.ArithmeticMean( |
300 perf_tests_helper.FlattenList( | 313 perf_tests_helper.FlattenList( |
301 stats.approximated_pixel_percentages)), 3) | 314 stats.approximated_pixel_percentages)), 3) |
302 else: | 315 else: |
303 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 316 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
304 return scalar.ScalarValue( | 317 return scalar.ScalarValue( |
305 page, 'mean_pixels_approximated', 'percent', mean_pixels_approximated, | 318 page, 'mean_pixels_approximated', 'percent', mean_pixels_approximated, |
306 description='Percentage of pixels that were approximated ' | 319 description='Percentage of pixels that were approximated ' |
307 '(checkerboarding, low-resolution tiles, etc.).', | 320 '(checkerboarding, low-resolution tiles, etc.).', |
308 none_value_reason=none_value_reason) | 321 none_value_reason=none_value_reason, |
| 322 improvement_direction=improvement_direction.DOWN) |
309 | 323 |
310 def _ComputeMeanPixelsCheckerboarded(self, page, stats): | 324 def _ComputeMeanPixelsCheckerboarded(self, page, stats): |
311 """Add the mean percentage of pixels checkerboarded. | 325 """Add the mean percentage of pixels checkerboarded. |
312 | 326 |
313 This looks at tiles which are only missing. | 327 This looks at tiles which are only missing. |
314 It does not take into consideration tiles which are of low or | 328 It does not take into consideration tiles which are of low or |
315 non-ideal resolution. | 329 non-ideal resolution. |
316 """ | 330 """ |
317 mean_pixels_checkerboarded = None | 331 mean_pixels_checkerboarded = None |
318 none_value_reason = None | 332 none_value_reason = None |
319 if self._HasEnoughFrames(stats.frame_timestamps): | 333 if self._HasEnoughFrames(stats.frame_timestamps): |
320 if rendering_stats.CHECKERBOARDED_PIXEL_ERROR in stats.errors: | 334 if rendering_stats.CHECKERBOARDED_PIXEL_ERROR in stats.errors: |
321 none_value_reason = stats.errors[ | 335 none_value_reason = stats.errors[ |
322 rendering_stats.CHECKERBOARDED_PIXEL_ERROR] | 336 rendering_stats.CHECKERBOARDED_PIXEL_ERROR] |
323 else: | 337 else: |
324 mean_pixels_checkerboarded = round(statistics.ArithmeticMean( | 338 mean_pixels_checkerboarded = round(statistics.ArithmeticMean( |
325 perf_tests_helper.FlattenList( | 339 perf_tests_helper.FlattenList( |
326 stats.checkerboarded_pixel_percentages)), 3) | 340 stats.checkerboarded_pixel_percentages)), 3) |
327 else: | 341 else: |
328 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE | 342 none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE |
329 return scalar.ScalarValue( | 343 return scalar.ScalarValue( |
330 page, 'mean_pixels_checkerboarded', 'percent', | 344 page, 'mean_pixels_checkerboarded', 'percent', |
331 mean_pixels_checkerboarded, | 345 mean_pixels_checkerboarded, |
332 description='Percentage of pixels that were checkerboarded.', | 346 description='Percentage of pixels that were checkerboarded.', |
333 none_value_reason=none_value_reason) | 347 none_value_reason=none_value_reason, |
| 348 improvement_direction=improvement_direction.DOWN) |
OLD | NEW |