Commit 2bc10e47 authored by miletus@chromium.org's avatar miletus@chromium.org

Report only one input latency metric mean_input_event_latency

As telemetry result is mainly meant for monitoring regression, exposing
different types of input latency, e.g. scroll/mouse/touch latency as we
currently do, does not add more benefit to this purpose but instead
adds more confusion for people when they look at the different input
latency metrics.

Lets only report one input latency metric mean_input_event_latency,
which is derived from all input events which have event creation timestamp
and swap buffer timestamp, regardless of its event type.

BUG=378027
TEST=run telemetry smoothness tests and see only one mean_input_event_latency is
reported. unittests pass.

Review URL: https://codereview.chromium.org/300273002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274390 0039d316-1c4b-4281-b951-d872f2087c98
parent ed3d748d
......@@ -106,19 +106,12 @@ class SmoothnessUnitTest(
self.assertEquals(len(mostly_smooth), 1)
self.assertGreaterEqual(mostly_smooth[0].GetRepresentativeNumber(), 0)
mean_mouse_wheel_latency = results.FindAllPageSpecificValuesNamed(
'mean_mouse_wheel_latency')
if mean_mouse_wheel_latency:
self.assertEquals(len(mean_mouse_wheel_latency), 1)
mean_input_event_latency = results.FindAllPageSpecificValuesNamed(
'mean_input_event_latency')
if mean_input_event_latency:
self.assertEquals(len(mean_input_event_latency), 1)
self.assertGreater(
mean_mouse_wheel_latency[0].GetRepresentativeNumber(), 0)
mean_touch_scroll_latency = results.FindAllPageSpecificValuesNamed(
'mean_touch_scroll_latency')
if mean_touch_scroll_latency:
self.assertEquals(len(mean_touch_scroll_latency), 1)
self.assertGreater(
mean_touch_scroll_latency[0].GetRepresentativeNumber(), 0)
mean_input_event_latency[0].GetRepresentativeNumber(), 0)
def testSmoothnessForPageWithNoGesture(self):
ps = self.CreateEmptyPageSet()
......
......@@ -30,76 +30,53 @@ class NotEnoughFramesError(page_measurement.MeasurementFailure):
'- Pages that can\'t be scrolled')
def GetScrollInputLatencyEvents(scroll_type, browser_process, timeline_range):
"""Get scroll events' LatencyInfo from the browser process's trace buffer
that are within the timeline_range.
def GetInputLatencyEvents(process, timeline_range):
"""Get input events' LatencyInfo from the process's trace buffer that are
within the timeline_range.
Scroll events (MouseWheel, GestureScrollUpdate or JS scroll on TouchMove)
dump their LatencyInfo into trace buffer as async trace event with name
"InputLatency". The trace event has a memeber 'step' containing its event
type and a memeber 'data' containing its latency history.
Input events dump their LatencyInfo into trace buffer as async trace event
with name "InputLatency". The trace event has a memeber 'data' containing
its latency history.
"""
scroll_events = []
if not browser_process:
return scroll_events
for event in browser_process.IterAllAsyncSlicesOfName("InputLatency"):
input_events = []
if not process:
return input_events
for event in process.IterAllAsyncSlicesOfName("InputLatency"):
if event.start >= timeline_range.min and event.end <= timeline_range.max:
for ss in event.sub_slices:
if 'step' not in ss.args:
continue
if 'data' not in ss.args:
continue
if ss.args['step'] == scroll_type:
scroll_events.append(ss)
return scroll_events
def ComputeMouseWheelScrollLatency(mouse_wheel_events):
""" Compute the mouse wheel scroll latency.
Mouse wheel scroll latency is the time from when mouse wheel event is sent
from browser RWH to renderer (the timestamp of component
'INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT') to when the scrolled page is
buffer swapped (the timestamp of component
'INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT')
"""
mouse_wheel_latency = []
for event in mouse_wheel_events:
data = event.args['data']
if BEGIN_COMP_NAME in data and END_COMP_NAME in data:
latency = data[END_COMP_NAME]['time'] - data[BEGIN_COMP_NAME]['time']
mouse_wheel_latency.append(latency / 1000.0)
return mouse_wheel_latency
def ComputeTouchScrollLatency(touch_scroll_events):
""" Compute the touch scroll latency.
Touch scroll latency is the time from when the touch event is created to
when the scrolled page is buffer swapped.
Touch event on differnt platforms uses different LatencyInfo component to
record its creation timestamp. On Aura, the creation time is kept in
'INPUT_EVENT_LATENCY_UI_COMPONENT' . On Android, the creation time is kept
in 'INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT'.
if 'data' in ss.args:
input_events.append(ss)
return input_events
def ComputeInputEventLatency(input_events):
""" Compute the input event latency.
Input event latency is the time from when the input event is created to
when its resulted page is swap buffered.
Input event on differnt platforms uses different LatencyInfo component to
record its creation timestamp. We go through the following component list
to find the creation timestamp:
1. INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT -- when event is created in OS
2. INPUT_EVENT_LATENCY_UI_COMPONENT -- when event reaches Chrome
3. INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT -- when event reaches RenderWidget
"""
touch_scroll_latency = []
for event in touch_scroll_events:
input_event_latency = []
for event in input_events:
data = event.args['data']
if END_COMP_NAME in data:
end_time = data[END_COMP_NAME]['time']
if UI_COMP_NAME in data and ORIGINAL_COMP_NAME in data:
raise ValueError, 'LatencyInfo has both UI and ORIGINAL component'
if UI_COMP_NAME in data:
latency = end_time - data[UI_COMP_NAME]['time']
touch_scroll_latency.append(latency / 1000.0)
elif ORIGINAL_COMP_NAME in data:
if ORIGINAL_COMP_NAME in data:
latency = end_time - data[ORIGINAL_COMP_NAME]['time']
touch_scroll_latency.append(latency / 1000.0)
return touch_scroll_latency
elif UI_COMP_NAME in data:
latency = end_time - data[UI_COMP_NAME]['time']
elif BEGIN_COMP_NAME in data:
latency = end_time - data[BEGIN_COMP_NAME]['time']
else:
raise ValueError, 'LatencyInfo has no begin component'
input_event_latency.append(latency / 1000.0)
return input_event_latency
def HasRenderingStats(process):
""" Returns True if the process contains at least one
......@@ -146,15 +123,9 @@ class RenderingStats(object):
self.rasterize_times = []
self.rasterized_pixel_counts = []
self.approximated_pixel_percentages = []
# End-to-end latency for MouseWheel scroll - from when mouse wheel event is
# generated to when the scrolled page is buffer swapped.
self.mouse_wheel_scroll_latency = []
# End-to-end latency for GestureScrollUpdate scroll - from when the touch
# event is generated to the scrolled page is buffer swapped.
self.touch_scroll_latency = []
# End-to-end latency for JS touch handler scrolling - from when the touch
# event is generated to the scrolled page is buffer swapped.
self.js_touch_scroll_latency = []
# End-to-end latency for input event - from when input event is
# generated to when the its resulted page is swap buffered.
self.input_event_latency = []
for timeline_range in timeline_ranges:
self.frame_timestamps.append([])
......@@ -166,9 +137,7 @@ class RenderingStats(object):
self.rasterize_times.append([])
self.rasterized_pixel_counts.append([])
self.approximated_pixel_percentages.append([])
self.mouse_wheel_scroll_latency.append([])
self.touch_scroll_latency.append([])
self.js_touch_scroll_latency.append([])
self.input_event_latency.append([])
if timeline_range.is_empty:
continue
......@@ -177,7 +146,8 @@ class RenderingStats(object):
renderer_process, timeline_range)
self._InitImplThreadRenderingStatsFromTimeline(
renderer_process, timeline_range)
self._InitScrollLatencyStatsFromTimeline(browser_process, timeline_range)
self._InitInputLatencyStatsFromTimeline(
browser_process, renderer_process, timeline_range)
# Check if we have collected at least 2 frames in every range. Otherwise we
# can't compute any meaningful metrics.
......@@ -185,21 +155,13 @@ class RenderingStats(object):
if len(segment) < 2:
raise NotEnoughFramesError(len(segment))
def _InitScrollLatencyStatsFromTimeline(
self, browser_process, timeline_range):
mouse_wheel_events = GetScrollInputLatencyEvents(
"MouseWheel", browser_process, timeline_range)
self.mouse_wheel_scroll_latency = ComputeMouseWheelScrollLatency(
mouse_wheel_events)
touch_scroll_events = GetScrollInputLatencyEvents(
"GestureScrollUpdate", browser_process, timeline_range)
self.touch_scroll_latency = ComputeTouchScrollLatency(touch_scroll_events)
js_touch_scroll_events = GetScrollInputLatencyEvents(
"TouchMove", browser_process, timeline_range)
self.js_touch_scroll_latency = ComputeTouchScrollLatency(
js_touch_scroll_events)
def _InitInputLatencyStatsFromTimeline(
self, browser_process, renderer_process, timeline_range):
latency_events = GetInputLatencyEvents(browser_process, timeline_range)
# Plugin input event's latency slice is generated in renderer process.
latency_events.extend(GetInputLatencyEvents(renderer_process,
timeline_range))
self.input_event_latency[-1] = ComputeInputEventLatency(latency_events)
def _GatherEvents(self, event_name, process, timeline_range):
events = []
......
......@@ -6,12 +6,9 @@ import random
import unittest
from telemetry.web_perf.metrics.rendering_stats import (
UI_COMP_NAME, BEGIN_COMP_NAME, END_COMP_NAME)
from telemetry.web_perf.metrics.rendering_stats import (
GetScrollInputLatencyEvents)
from telemetry.web_perf.metrics.rendering_stats import (
ComputeMouseWheelScrollLatency)
from telemetry.web_perf.metrics.rendering_stats import ComputeTouchScrollLatency
UI_COMP_NAME, BEGIN_COMP_NAME, ORIGINAL_COMP_NAME, END_COMP_NAME)
from telemetry.web_perf.metrics.rendering_stats import ComputeInputEventLatency
from telemetry.web_perf.metrics.rendering_stats import GetInputLatencyEvents
from telemetry.web_perf.metrics.rendering_stats import HasRenderingStats
from telemetry.web_perf.metrics.rendering_stats import RenderingStats
from telemetry.web_perf.metrics.rendering_stats import NotEnoughFramesError
......@@ -67,12 +64,8 @@ class ReferenceRenderingStats(object):
class ReferenceInputLatencyStats(object):
""" Stores expected data for comparison with actual input latency stats """
def __init__(self):
self.mouse_wheel_scroll_latency = []
self.touch_scroll_latency = []
self.js_touch_scroll_latency = []
self.mouse_wheel_scroll_events = []
self.touch_scroll_events = []
self.js_touch_scroll_events = []
self.input_event_latency = []
self.input_event = []
def AddMainThreadRenderingStats(mock_timer, thread, first_frame,
ref_stats = None):
......@@ -155,16 +148,17 @@ def AddImplThreadRenderingStats(mock_timer, thread, first_frame,
data['visible_content_area']) * 100.0, 3))
def AddInputLatencyStats(mock_timer, input_type, start_thread, end_thread,
def AddInputLatencyStats(mock_timer, start_thread, end_thread,
ref_latency_stats = None):
""" Adds a random input latency stats event.
input_type: The input type for which the latency slice is generated.
start_thread: The start thread on which the async slice is added.
end_thread: The end thread on which the async slice is ended.
ref_latency_stats: A ReferenceInputLatencyStats object for expected values.
"""
mock_timer.Advance(2, 4)
original_comp_time = mock_timer.Get() * 1000.0
mock_timer.Advance(2, 4)
ui_comp_time = mock_timer.Get() * 1000.0
mock_timer.Advance(2, 4)
......@@ -172,7 +166,8 @@ def AddInputLatencyStats(mock_timer, input_type, start_thread, end_thread,
mock_timer.Advance(10, 20)
end_comp_time = mock_timer.Get() * 1000.0
data = { UI_COMP_NAME: {'time': ui_comp_time},
data = { ORIGINAL_COMP_NAME: {'time': original_comp_time},
UI_COMP_NAME: {'time': ui_comp_time},
BEGIN_COMP_NAME: {'time': begin_comp_time},
END_COMP_NAME: {'time': end_comp_time} }
......@@ -183,7 +178,7 @@ def AddInputLatencyStats(mock_timer, input_type, start_thread, end_thread,
async_sub_slice = tracing_async_slice.AsyncSlice(
'benchmark', 'InputLatency', timestamp)
async_sub_slice.args = {'data': data, 'step': input_type}
async_sub_slice.args = {'data': data}
async_sub_slice.parent_slice = async_slice
async_sub_slice.start_thread = start_thread
async_sub_slice.end_thread = end_thread
......@@ -196,20 +191,9 @@ def AddInputLatencyStats(mock_timer, input_type, start_thread, end_thread,
if not ref_latency_stats:
return
if input_type == 'MouseWheel':
ref_latency_stats.mouse_wheel_scroll_events.append(async_sub_slice)
ref_latency_stats.mouse_wheel_scroll_latency.append(
(data[END_COMP_NAME]['time'] - data[BEGIN_COMP_NAME]['time']) / 1000.0)
if input_type == 'GestureScrollUpdate':
ref_latency_stats.touch_scroll_events.append(async_sub_slice)
ref_latency_stats.touch_scroll_latency.append(
(data[END_COMP_NAME]['time'] - data[UI_COMP_NAME]['time']) / 1000.0)
if input_type == 'TouchMove':
ref_latency_stats.js_touch_scroll_events.append(async_sub_slice)
ref_latency_stats.js_touch_scroll_latency.append(
(data[END_COMP_NAME]['time'] - data[UI_COMP_NAME]['time']) / 1000.0)
ref_latency_stats.input_event.append(async_sub_slice)
ref_latency_stats.input_event_latency.append(
(data[END_COMP_NAME]['time'] - data[ORIGINAL_COMP_NAME]['time']) / 1000.0)
class RenderingStatsUnitTest(unittest.TestCase):
def testHasRenderingStats(self):
......@@ -379,7 +363,7 @@ class RenderingStatsUnitTest(unittest.TestCase):
self.assertEquals(stats.recorded_pixel_counts,
renderer_ref_stats.recorded_pixel_counts)
def testScrollLatencyFromTimeline(self):
def testInputLatencyFromTimeline(self):
timeline = model.TimelineModel()
# Create a browser process and a renderer process.
......@@ -389,41 +373,26 @@ class RenderingStatsUnitTest(unittest.TestCase):
renderer_main = renderer.GetOrCreateThread(tid = 21)
timer = MockTimer()
ref_latency_stats = ReferenceInputLatencyStats()
ref_latency = ReferenceInputLatencyStats()
# Create 10 input latency stats events for Action A.
timer.Advance(2, 4)
renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '')
for _ in xrange(0, 10):
AddInputLatencyStats(timer, 'MouseWheel', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, 'TouchMove', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
timer.Advance(2, 4)
renderer_main.EndSlice(timer.Get())
# Create 5 input latency stats events not within any action.
timer.Advance(2, 4)
for _ in xrange(0, 5):
AddInputLatencyStats(timer, 'MouseWheel', browser_main,
renderer_main, None)
AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main,
renderer_main, None)
AddInputLatencyStats(timer, 'TouchMove', browser_main,
renderer_main, None)
AddInputLatencyStats(timer, browser_main, renderer_main, None)
# Create 10 input latency stats events for Action B.
timer.Advance(2, 4)
renderer_main.BeginSlice('webkit.console', 'ActionB', timer.Get(), '')
for _ in xrange(0, 10):
AddInputLatencyStats(timer, 'MouseWheel', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, 'TouchMove', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
timer.Advance(2, 4)
renderer_main.EndSlice(timer.Get())
......@@ -431,21 +400,14 @@ class RenderingStatsUnitTest(unittest.TestCase):
timer.Advance(2, 4)
renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '')
for _ in xrange(0, 10):
AddInputLatencyStats(timer, 'MouseWheel', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, 'TouchMove', browser_main,
renderer_main, ref_latency_stats)
AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
timer.Advance(2, 4)
renderer_main.EndSlice(timer.Get())
browser.FinalizeImport()
renderer.FinalizeImport()
mouse_wheel_scroll_events = []
touch_scroll_events = []
js_touch_scroll_events = []
input_events = []
timeline_markers = timeline.FindTimelineMarkers(
['ActionA', 'ActionB', 'ActionA'])
......@@ -453,25 +415,8 @@ class RenderingStatsUnitTest(unittest.TestCase):
for marker in timeline_markers ]:
if timeline_range.is_empty:
continue
tmp_mouse_events = GetScrollInputLatencyEvents(
'MouseWheel', browser, timeline_range)
tmp_touch_scroll_events = GetScrollInputLatencyEvents(
'GestureScrollUpdate', browser, timeline_range)
tmp_js_touch_scroll_events = GetScrollInputLatencyEvents(
'TouchMove', browser, timeline_range)
mouse_wheel_scroll_events.extend(tmp_mouse_events)
touch_scroll_events.extend(tmp_touch_scroll_events)
js_touch_scroll_events.extend(tmp_js_touch_scroll_events)
self.assertEquals(mouse_wheel_scroll_events,
ref_latency_stats.mouse_wheel_scroll_events)
self.assertEquals(touch_scroll_events,
ref_latency_stats.touch_scroll_events)
self.assertEquals(js_touch_scroll_events,
ref_latency_stats.js_touch_scroll_events)
self.assertEquals(ComputeMouseWheelScrollLatency(mouse_wheel_scroll_events),
ref_latency_stats.mouse_wheel_scroll_latency)
self.assertEquals(ComputeTouchScrollLatency(touch_scroll_events),
ref_latency_stats.touch_scroll_latency)
self.assertEquals(ComputeTouchScrollLatency(js_touch_scroll_events),
ref_latency_stats.js_touch_scroll_latency)
input_events.extend(GetInputLatencyEvents(browser, timeline_range))
self.assertEquals(input_events, ref_latency.input_event)
self.assertEquals(ComputeInputEventLatency(input_events),
ref_latency.input_event_latency)
......@@ -18,35 +18,17 @@ class SmoothnessMetric(timeline_based_metric.TimelineBasedMetric):
stats = rendering_stats.RenderingStats(
renderer_process, model.browser_process,
[r.GetBounds() for r in interaction_records])
if stats.mouse_wheel_scroll_latency:
mean_mouse_wheel_scroll_latency = statistics.ArithmeticMean(
stats.mouse_wheel_scroll_latency)
mouse_wheel_scroll_latency_discrepancy = statistics.DurationsDiscrepancy(
stats.mouse_wheel_scroll_latency)
results.Add('mean_mouse_wheel_scroll_latency', 'ms',
round(mean_mouse_wheel_scroll_latency, 3))
results.Add('mouse_wheel_scroll_latency_discrepancy', 'ms',
round(mouse_wheel_scroll_latency_discrepancy, 4))
if stats.touch_scroll_latency:
mean_touch_scroll_latency = statistics.ArithmeticMean(
stats.touch_scroll_latency)
touch_scroll_latency_discrepancy = statistics.DurationsDiscrepancy(
stats.touch_scroll_latency)
results.Add('mean_touch_scroll_latency', 'ms',
round(mean_touch_scroll_latency, 3))
results.Add('touch_scroll_latency_discrepancy', 'ms',
round(touch_scroll_latency_discrepancy, 4))
if stats.js_touch_scroll_latency:
mean_js_touch_scroll_latency = statistics.ArithmeticMean(
stats.js_touch_scroll_latency)
js_touch_scroll_latency_discrepancy = statistics.DurationsDiscrepancy(
stats.js_touch_scroll_latency)
results.Add('mean_js_touch_scroll_latency', 'ms',
round(mean_js_touch_scroll_latency, 3))
results.Add('js_touch_scroll_latency_discrepancy', 'ms',
round(js_touch_scroll_latency_discrepancy, 4))
input_event_latency = FlattenList(stats.input_event_latency)
if input_event_latency:
mean_input_event_latency = statistics.ArithmeticMean(
input_event_latency)
input_event_latency_discrepancy = statistics.DurationsDiscrepancy(
input_event_latency)
results.Add('mean_input_event_latency', 'ms',
round(mean_input_event_latency, 3))
results.Add('input_event_latency_discrepancy', 'ms',
round(input_event_latency_discrepancy, 4))
# List of raw frame times.
frame_times = FlattenList(stats.frame_times)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment