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

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

Issue 805393003: [Telemetry] Remove old paint-rect speed index implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years 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 | tools/perf/metrics/speedindex_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 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 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 collections 5 import collections
6 6
7 from metrics import Metric 7 from metrics import Metric
8 from telemetry.image_processing import image_util 8 from telemetry.image_processing import image_util
9 from telemetry.image_processing import rgba_color 9 from telemetry.image_processing import rgba_color
10 from telemetry.value import scalar 10 from telemetry.value import scalar
(...skipping 18 matching lines...) Expand all
29 def CustomizeBrowserOptions(cls, options): 29 def CustomizeBrowserOptions(cls, options):
30 options.AppendExtraBrowserArgs('--disable-infobars') 30 options.AppendExtraBrowserArgs('--disable-infobars')
31 31
32 def Start(self, _, tab): 32 def Start(self, _, tab):
33 """Start recording events. 33 """Start recording events.
34 34
35 This method should be called in the WillNavigateToPage method of 35 This method should be called in the WillNavigateToPage method of
36 a PageTest, so that all the events can be captured. If it's called 36 a PageTest, so that all the events can be captured. If it's called
37 in DidNavigateToPage, that will be too late. 37 in DidNavigateToPage, that will be too late.
38 """ 38 """
39 self._impl = (VideoSpeedIndexImpl() if tab.video_capture_supported else 39 if not tab.video_capture_supported:
40 PaintRectSpeedIndexImpl()) 40 raise NotImplementedError('SpeedIndex requires video capture support')
chrishenry 2014/12/20 01:21:04 Should this raise or add none values (for each of
tonyg 2015/01/06 21:48:27 Makes sense. PTAL
41 self._impl = VideoSpeedIndexImpl()
41 self._impl.Start(tab) 42 self._impl.Start(tab)
42 43
43 def Stop(self, _, tab): 44 def Stop(self, _, tab):
44 """Stop timeline recording.""" 45 """Stop recording."""
45 assert self._impl, 'Must call Start() before Stop()' 46 assert self._impl, 'Must call Start() before Stop()'
46 assert self.IsFinished(tab), 'Must wait for IsFinished() before Stop()' 47 assert self.IsFinished(tab), 'Must wait for IsFinished() before Stop()'
47 self._impl.Stop(tab) 48 self._impl.Stop(tab)
48 49
49 # Optional argument chart_name is not in base class Metric. 50 # Optional argument chart_name is not in base class Metric.
50 # pylint: disable=W0221 51 # pylint: disable=W0221
51 def AddResults(self, tab, results, chart_name=None): 52 def AddResults(self, tab, results, chart_name=None):
52 """Calculate the speed index and add it to the results.""" 53 """Calculate the speed index and add it to the results."""
53 index = self._impl.CalculateSpeedIndex(tab) 54 index = self._impl.CalculateSpeedIndex(tab)
54 # Release the tab so that it can be disconnected. 55 # Release the tab so that it can be disconnected.
(...skipping 11 matching lines...) Expand all
66 'get the features of performance more accurately than load ' 67 'get the features of performance more accurately than load '
67 'time because the load time will measure the time when ' 68 'time because the load time will measure the time when '
68 'static resources are loaded. If you want to get more ' 69 'static resources are loaded. If you want to get more '
69 'detail, please refer to http://goo.gl/Rw3d5d. Currently ' 70 'detail, please refer to http://goo.gl/Rw3d5d. Currently '
70 'there are two implementations: for Android and for ' 71 'there are two implementations: for Android and for '
71 'Desktop. The Android version uses video capture; the ' 72 'Desktop. The Android version uses video capture; the '
72 'Desktop one uses paint events and has extra overhead to ' 73 'Desktop one uses paint events and has extra overhead to '
73 'catch paint events.')) 74 'catch paint events.'))
74 75
75 def IsFinished(self, tab): 76 def IsFinished(self, tab):
76 """Decide whether the timeline recording should be stopped. 77 """Decide whether the recording should be stopped.
77
78 When the timeline recording is stopped determines which paint events
79 are used in the speed index metric calculation. In general, the recording
80 should continue if there has just been some data received, because
81 this suggests that painting may continue.
82 78
83 A page may repeatedly request resources in an infinite loop; a timeout 79 A page may repeatedly request resources in an infinite loop; a timeout
84 should be placed in any measurement that uses this metric, e.g.: 80 should be placed in any measurement that uses this metric, e.g.:
85 def IsDone(): 81 def IsDone():
86 return self._speedindex.IsFinished(tab) 82 return self._speedindex.IsFinished(tab)
87 util.WaitFor(IsDone, 60) 83 util.WaitFor(IsDone, 60)
88 84
89 Returns: 85 Returns:
90 True if 2 seconds have passed since last resource received, false 86 True if 2 seconds have passed since last resource received, false
91 otherwise. 87 otherwise.
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 else: 171 else:
176 return 0.0 172 return 0.0
177 return 1 - histogram.Distance(final_histogram) / total_distance 173 return 1 - histogram.Distance(final_histogram) / total_distance
178 174
179 self._time_completeness_list = [(time, FrameProgress(hist)) 175 self._time_completeness_list = [(time, FrameProgress(hist))
180 for time, hist in histograms] 176 for time, hist in histograms]
181 177
182 def GetTimeCompletenessList(self, tab): 178 def GetTimeCompletenessList(self, tab):
183 assert self._time_completeness_list, 'Must call Stop() first.' 179 assert self._time_completeness_list, 'Must call Stop() first.'
184 return self._time_completeness_list 180 return self._time_completeness_list
185
186
187 class PaintRectSpeedIndexImpl(SpeedIndexImpl):
188
189 def __init__(self):
190 super(PaintRectSpeedIndexImpl, self).__init__()
191
192 def Start(self, tab):
193 tab.StartTimelineRecording()
194
195 def Stop(self, tab):
196 tab.StopTimelineRecording()
197
198 def GetTimeCompletenessList(self, tab):
199 events = tab.timeline_model.GetAllEvents()
200 viewport = self._GetViewportSize(tab)
201 paint_events = self._IncludedPaintEvents(events)
202 time_area_dict = self._TimeAreaDict(paint_events, viewport)
203 total_area = sum(time_area_dict.values())
204 assert total_area > 0.0, 'Total paint event area must be greater than 0.'
205 completeness = 0.0
206 time_completeness_list = []
207
208 # TODO(tonyg): This sets the start time to the start of the first paint
209 # event. That can't be correct. The start time should be navigationStart.
210 # Since the previous screen is not cleared at navigationStart, we should
211 # probably assume the completeness is 0 until the first paint and add the
212 # time of navigationStart as the start. We need to confirm what WPT does.
213 time_completeness_list.append(
214 (tab.timeline_model.GetAllEvents()[0].start, completeness))
215
216 for time, area in sorted(time_area_dict.items()):
217 completeness += float(area) / total_area
218 # Visual progress is rounded to the nearest percentage point as in WPT.
219 time_completeness_list.append((time, round(completeness, 2)))
220 return time_completeness_list
221
222 def _GetViewportSize(self, tab):
223 """Returns dimensions of the viewport."""
224 return tab.EvaluateJavaScript('[ window.innerWidth, window.innerHeight ]')
225
226 def _IncludedPaintEvents(self, events):
227 """Get all events that are counted in the calculation of the speed index.
228
229 There's one category of paint event that's filtered out: paint events
230 that occur before the first 'ResourceReceiveResponse' and 'Layout' events.
231
232 Previously in the WPT speed index, paint events that contain children paint
233 events were also filtered out.
234 """
235 def FirstLayoutTime(events):
236 """Get the start time of the first layout after a resource received."""
237 has_received_response = False
238 for event in events:
239 if event.name == 'ResourceReceiveResponse':
240 has_received_response = True
241 elif has_received_response and event.name == 'Layout':
242 return event.start
243 assert False, 'There were no layout events after resource receive events.'
244
245 first_layout_time = FirstLayoutTime(events)
246 paint_events = [e for e in events
247 if e.start >= first_layout_time and e.name == 'Paint']
248 return paint_events
249
250 def _TimeAreaDict(self, paint_events, viewport):
251 """Make a dict from time to adjusted area value for events at that time.
252
253 The adjusted area value of each paint event is determined by how many paint
254 events cover the same rectangle, and whether it's a full-window paint event.
255 "Adjusted area" can also be thought of as "points" of visual completeness --
256 each rectangle has a certain number of points and these points are
257 distributed amongst the paint events that paint that rectangle.
258
259 Args:
260 paint_events: A list of paint events
261 viewport: A tuple (width, height) of the window.
262
263 Returns:
264 A dictionary of times of each paint event (in milliseconds) to the
265 adjusted area that the paint event is worth.
266 """
267 width, height = viewport
268 fullscreen_area = width * height
269
270 def ClippedArea(rectangle):
271 """Returns rectangle area clipped to viewport size."""
272 _, x0, y0, x1, y1 = rectangle
273 clipped_width = max(0, min(width, x1) - max(0, x0))
274 clipped_height = max(0, min(height, y1) - max(0, y0))
275 return clipped_width * clipped_height
276
277 grouped = self._GroupEventByRectangle(paint_events)
278 event_area_dict = collections.defaultdict(int)
279
280 for rectangle, events in grouped.items():
281 # The area points for each rectangle are divided up among the paint
282 # events in that rectangle.
283 area = ClippedArea(rectangle)
284 update_count = len(events)
285 adjusted_area = float(area) / update_count
286
287 # Paint events for the largest-area rectangle are counted as 50%.
288 if area == fullscreen_area:
289 adjusted_area /= 2
290
291 for event in events:
292 # The end time for an event is used for that event's time.
293 event_time = event.end
294 event_area_dict[event_time] += adjusted_area
295
296 return event_area_dict
297
298 def _GetRectangle(self, paint_event):
299 """Get the specific rectangle on the screen for a paint event.
300
301 Each paint event belongs to a frame (as in html <frame> or <iframe>).
302 This, together with location and dimensions, comprises a rectangle.
303 In the WPT source, this 'rectangle' is also called a 'region'.
304 """
305 def GetBox(quad):
306 """Gets top-left and bottom-right coordinates from paint event.
307
308 In the timeline data from devtools, paint rectangle dimensions are
309 represented x-y coordinates of four corners, clockwise from the top-left.
310 See: function WebInspector.TimelinePresentationModel.quadFromRectData
311 in file src/out/Debug/obj/gen/devtools/TimelinePanel.js.
312 """
313 x0, y0, _, _, x1, y1, _, _ = quad
314 return (x0, y0, x1, y1)
315
316 assert paint_event.name == 'Paint'
317 frame = paint_event.args['frameId']
318 return (frame,) + GetBox(paint_event.args['data']['clip'])
319
320 def _GroupEventByRectangle(self, paint_events):
321 """Group all paint events according to the rectangle that they update."""
322 result = collections.defaultdict(list)
323 for event in paint_events:
324 assert event.name == 'Paint'
325 result[self._GetRectangle(event)].append(event)
326 return result
OLDNEW
« no previous file with comments | « no previous file | tools/perf/metrics/speedindex_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698