Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / tools / perf / metrics / speedindex.py
blob5d7fa3d7a478923b7c07d20930d6fa37a2affe48
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
3 # found in the LICENSE file.
5 from telemetry.util import image_util
6 from telemetry.util import rgba_color
7 from telemetry.value import scalar
9 from metrics import Metric
12 class SpeedIndexMetric(Metric):
13 """The speed index metric is one way of measuring page load speed.
15 It is meant to approximate user perception of page load speed, and it
16 is based on the amount of time that it takes to paint to the visual
17 portion of the screen. It includes paint events that occur after the
18 onload event, and it doesn't include time loading things off-screen.
20 This speed index metric is based on WebPageTest.org (WPT).
21 For more info see: http://goo.gl/e7AH5l
22 """
23 def __init__(self):
24 super(SpeedIndexMetric, self).__init__()
25 self._impl = None
27 @classmethod
28 def CustomizeBrowserOptions(cls, options):
29 options.AppendExtraBrowserArgs('--disable-infobars')
31 def Start(self, _, tab):
32 """Start recording events.
34 This method should be called in the WillNavigateToPage method of
35 a PageTest, so that all the events can be captured. If it's called
36 in DidNavigateToPage, that will be too late.
37 """
38 if not tab.video_capture_supported:
39 return
40 self._impl = VideoSpeedIndexImpl()
41 self._impl.Start(tab)
43 def Stop(self, _, tab):
44 """Stop recording."""
45 if not tab.video_capture_supported:
46 return
47 assert self._impl, 'Must call Start() before Stop()'
48 assert self.IsFinished(tab), 'Must wait for IsFinished() before Stop()'
49 self._impl.Stop(tab)
51 # Optional argument chart_name is not in base class Metric.
52 # pylint: disable=W0221
53 def AddResults(self, tab, results, chart_name=None):
54 """Calculate the speed index and add it to the results."""
55 try:
56 if tab.video_capture_supported:
57 index = self._impl.CalculateSpeedIndex(tab)
58 none_value_reason = None
59 else:
60 index = None
61 none_value_reason = 'Video capture is not supported.'
62 finally:
63 self._impl = None # Release the tab so that it can be disconnected.
65 results.AddValue(scalar.ScalarValue(
66 results.current_page, '%s_speed_index' % chart_name, 'ms', index,
67 description='Speed Index. This focuses on time when visible parts of '
68 'page are displayed and shows the time when the '
69 'first look is "almost" composed. If the contents of the '
70 'testing page are composed by only static resources, load '
71 'time can measure more accurately and speed index will be '
72 'smaller than the load time. On the other hand, If the '
73 'contents are composed by many XHR requests with small '
74 'main resource and javascript, speed index will be able to '
75 'get the features of performance more accurately than load '
76 'time because the load time will measure the time when '
77 'static resources are loaded. If you want to get more '
78 'detail, please refer to http://goo.gl/Rw3d5d. Currently '
79 'there are two implementations: for Android and for '
80 'Desktop. The Android version uses video capture; the '
81 'Desktop one uses paint events and has extra overhead to '
82 'catch paint events.', none_value_reason=none_value_reason))
84 def IsFinished(self, tab):
85 """Decide whether the recording should be stopped.
87 A page may repeatedly request resources in an infinite loop; a timeout
88 should be placed in any measurement that uses this metric, e.g.:
89 def IsDone():
90 return self._speedindex.IsFinished(tab)
91 util.WaitFor(IsDone, 60)
93 Returns:
94 True if 2 seconds have passed since last resource received, false
95 otherwise.
96 """
97 return tab.HasReachedQuiescence()
100 class SpeedIndexImpl(object):
102 def Start(self, tab):
103 raise NotImplementedError()
105 def Stop(self, tab):
106 raise NotImplementedError()
108 def GetTimeCompletenessList(self, tab):
109 """Returns a list of time to visual completeness tuples.
111 In the WPT PHP implementation, this is also called 'visual progress'.
113 raise NotImplementedError()
115 def CalculateSpeedIndex(self, tab):
116 """Calculate the speed index.
118 The speed index number conceptually represents the number of milliseconds
119 that the page was "visually incomplete". If the page were 0% complete for
120 1000 ms, then the score would be 1000; if it were 0% complete for 100 ms
121 then 90% complete (ie 10% incomplete) for 900 ms, then the score would be
122 1.0*100 + 0.1*900 = 190.
124 Returns:
125 A single number, milliseconds of visual incompleteness.
127 time_completeness_list = self.GetTimeCompletenessList(tab)
128 prev_completeness = 0.0
129 speed_index = 0.0
130 prev_time = time_completeness_list[0][0]
131 for time, completeness in time_completeness_list:
132 # Add the incemental value for the interval just before this event.
133 elapsed_time = time - prev_time
134 incompleteness = (1.0 - prev_completeness)
135 speed_index += elapsed_time * incompleteness
137 # Update variables for next iteration.
138 prev_completeness = completeness
139 prev_time = time
140 return int(speed_index)
143 class VideoSpeedIndexImpl(SpeedIndexImpl):
145 def __init__(self, image_util_module=image_util):
146 # Allow image_util to be passed in so we can fake it out for testing.
147 super(VideoSpeedIndexImpl, self).__init__()
148 self._time_completeness_list = None
149 self._image_util_module = image_util_module
151 def Start(self, tab):
152 assert tab.video_capture_supported
153 # Blank out the current page so it doesn't count towards the new page's
154 # completeness.
155 tab.Highlight(rgba_color.WHITE)
156 # TODO(tonyg): Bitrate is arbitrary here. Experiment with screen capture
157 # overhead vs. speed index accuracy and set the bitrate appropriately.
158 tab.StartVideoCapture(min_bitrate_mbps=4)
160 def Stop(self, tab):
161 # Ignore white because Chrome may blank out the page during load and we want
162 # that to count as 0% complete. Relying on this fact, we also blank out the
163 # previous page to white. The tolerance of 8 experimentally does well with
164 # video capture at 4mbps. We should keep this as low as possible with
165 # supported video compression settings.
166 video_capture = tab.StopVideoCapture()
167 histograms = [(time, self._image_util_module.GetColorHistogram(
168 image, ignore_color=rgba_color.WHITE, tolerance=8))
169 for time, image in video_capture.GetVideoFrameIter()]
171 start_histogram = histograms[0][1]
172 final_histogram = histograms[-1][1]
173 total_distance = start_histogram.Distance(final_histogram)
175 def FrameProgress(histogram):
176 if total_distance == 0:
177 if histogram.Distance(final_histogram) == 0:
178 return 1.0
179 else:
180 return 0.0
181 return 1 - histogram.Distance(final_histogram) / total_distance
183 self._time_completeness_list = [(time, FrameProgress(hist))
184 for time, hist in histograms]
186 def GetTimeCompletenessList(self, tab):
187 assert self._time_completeness_list, 'Must call Stop() first.'
188 return self._time_completeness_list