Commit 97171c78 authored by skyostil's avatar skyostil Committed by Commit bot

Revert of [telemetry] Add a V8GCTimes measurement and benchmarks. (patchset #2...

Revert of [telemetry] Add a V8GCTimes measurement and benchmarks. (patchset #2 id:20001 of https://codereview.chromium.org/898673005/)

Reason for revert:
Failing on mac:

http://chromegw/i/chromium.perf/builders/Mac%2010.9%20Perf%20%281%29/builds/2017

Original issue's description:
> [telemetry] Add a V8GCTimes measurement and benchmarks.
>
> Replaces the v8_stats metric with a v8_gc_times
> measurement.  This new measurement measures the amount of
> time spent in GC during the whole benchmark rather than
> just during gestures.
>
> The following gc metrics are measured:
>   v8_gc_incremental_marking
>   v8_gc_incremental_marking_idle_deadline_overrun
>   v8_gc_incremental_marking_outside_idle
>   v8_gc_incremental_marking_percentage_idle
>   v8_gc_mark_compactor
>   v8_gc_mark_compactor_idle_deadline_overrun
>   v8_gc_mark_compactor_outside_idle
>   v8_gc_mark_compactor_percentage_idle
>   v8_gc_scavenger
>   v8_gc_scavenger_idle_deadline_overrun
>   v8_gc_scavenger_outside_idle
>   v8_gc_scavenger_percentage_idle
>   v8_gc_total
>   v8_gc_total_idle_deadline_overrun
>   v8_gc_total_outside_idle
>
> It also measures the duration of the measurement and the
> amount of CPU time used.
>
> The v8_garbage_collection_cases benchmark is modified to
> use this new v8_gc_times measurement, and v8.top_25_smooth
> and v8.key_mobile_sites_smooth benchmarks have been added.
>
> Since v8_garbage_collection_cases was the only benchmark to
> use the is_fast metric type, and these measurements are now
> done as part of the v8_gc_times measurement now, this CL
> also removes the fast metric type.
>
> Perf Sherrifs: Since this CL changes he way in which the GC
> measurements are done it may cause a movement in the
> v8_garbage_collection_cases benchmarks.
>
> Committed: https://crrev.com/452de5927e54e1da0d57a4b2df8b447f4d442360
> Cr-Commit-Position: refs/heads/master@{#314868}

TBR=hpayer@chromium.org,rmcilroy@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

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

Cr-Commit-Position: refs/heads/master@{#315035}
parent 78fa7c1c
...@@ -3,40 +3,20 @@ ...@@ -3,40 +3,20 @@
# found in the LICENSE file. # found in the LICENSE file.
import page_sets import page_sets
from measurements import v8_gc_times from telemetry.web_perf import timeline_based_measurement
from telemetry import benchmark from telemetry import benchmark
@benchmark.Disabled('win') # crbug.com/416502 @benchmark.Disabled('win') # crbug.com/416502
class V8GarbageCollectionCases(benchmark.Benchmark): class V8GarbageCollectionCases(benchmark.Benchmark):
"""Measure V8 GC metrics on the garbage collection cases.""" """Measure V8 metrics on the garbage collection cases."""
test = v8_gc_times.V8GCTimes def CreateTimelineBasedMeasurementOptions(self):
page_set = page_sets.GarbageCollectionCasesPageSet # TODO(ernstm): Remove v8-overhead when benchmark relevant v8 events become
# available in the 'benchmark' category.
return timeline_based_measurement.Options(overhead_level='v8-overhead')
@classmethod @classmethod
def Name(cls): def Name(cls):
return 'v8.garbage_collection_cases' return 'v8.garbage_collection_cases'
@benchmark.Disabled('win') # crbug.com/416502 page_set = page_sets.GarbageCollectionCasesPageSet
class V8Top25(benchmark.Benchmark):
"""Measures V8 GC metrics on the while scrolling down the top 25 web pages.
http://www.chromium.org/developers/design-documents/rendering-benchmarks"""
test = v8_gc_times.V8GCTimes
page_set = page_sets.Top25SmoothPageSet
@classmethod
def Name(cls):
return 'v8.top_25_smooth'
@benchmark.Enabled('android')
class V8KeyMobileSites(benchmark.Benchmark):
"""Measures V8 GC metrics on the while scrolling down key mobile sites.
http://www.chromium.org/developers/design-documents/rendering-benchmarks"""
test = v8_gc_times.V8GCTimes
page_set = page_sets.KeyMobileSitesSmoothPageSet
@classmethod
def Name(cls):
return 'v8.key_mobile_sites_smooth'
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from telemetry.core.platform import tracing_category_filter
from telemetry.core.platform import tracing_options
from telemetry.page import page_test
from telemetry.timeline.model import TimelineModel
from telemetry.util import statistics
from telemetry.value import scalar
class V8GCTimes(page_test.PageTest):
_TIME_OUT_IN_SECONDS = 60
_CATEGORIES = ['blink.console',
'renderer.scheduler',
'toplevel',
'v8',
'webkit.console']
_RENDERER_MAIN_THREAD = 'CrRendererMain'
_IDLE_TASK_PARENT = 'SingleThreadIdleTaskRunner::RunTask'
def __init__(self):
super(V8GCTimes, self).__init__('RunPageInteractions')
self._v8_event_stats = [
V8EventStat('V8.GCIncrementalMarking',
'v8_gc_incremental_marking',
'incremental marking steps'),
V8EventStat('V8.GCScavenger',
'v8_gc_scavenger',
'scavenges'),
V8EventStat('V8.GCCompactor',
'v8_gc_mark_compactor',
'mark-sweep-compactor')]
self._renderer_process = None
def WillNavigateToPage(self, page, tab):
category_filter = tracing_category_filter.TracingCategoryFilter()
for category in self._CATEGORIES:
category_filter.AddIncludedCategory(category)
options = tracing_options.TracingOptions()
options.enable_chrome_trace = True
tab.browser.platform.tracing_controller.Start(
options, category_filter, self._TIME_OUT_IN_SECONDS)
def DidRunActions(self, page, tab):
trace_data = tab.browser.platform.tracing_controller.Stop()
timeline_model = TimelineModel(trace_data)
self._renderer_process = timeline_model.GetRendererProcessFromTabId(tab.id)
def ValidateAndMeasurePage(self, page, tab, results):
self._AddV8MetricsToResults(self._renderer_process, results)
def _AddV8MetricsToResults(self, process, results):
if process is None:
return
for thread in process.threads.values():
if thread.name != self._RENDERER_MAIN_THREAD:
continue
self._AddV8EventStatsToResults(thread, results)
self._AddCpuTimeStatsToResults(thread, results)
def _AddV8EventStatsToResults(self, thread, results):
# Find all V8 GC events in the trace.
for event in thread.IterAllSlices():
event_stat = _FindV8EventStatForEvent(self._v8_event_stats, event.name)
if not event_stat:
continue
event_stat.thread_duration += event.thread_duration
parent_idle_task = _ParentIdleTask(event)
if parent_idle_task:
allotted_idle_time = parent_idle_task.args['allotted_time_ms']
idle_task_wall_overrun = 0
if event.duration > allotted_idle_time:
idle_task_wall_overrun = event.duration - allotted_idle_time
# Don't count time over the deadline as being inside idle time.
# Since the deadline should be relative to wall clock we compare
# allotted_time_ms with wall duration instead of thread duration, and
# then assume the thread duration was inside idle for the same
# percentage of time.
inside_idle = event.thread_duration * statistics.DivideIfPossibleOrZero(
event.duration - idle_task_wall_overrun, event.duration)
event_stat.thread_duration_inside_idle += inside_idle
event_stat.idle_task_overrun_duration += idle_task_wall_overrun
for v8_event_stat in self._v8_event_stats:
results.AddValue(scalar.ScalarValue(
results.current_page, v8_event_stat.result_name, 'ms',
v8_event_stat.thread_duration,
('Total thread duration spent in %s' %
v8_event_stat.result_description)))
results.AddValue(scalar.ScalarValue(results.current_page,
'%s_outside_idle' % v8_event_stat.result_name, 'ms',
v8_event_stat.thread_duration_outside_idle,
('Total thread duration spent in %s outside of idle tasks' %
v8_event_stat.result_description)))
results.AddValue(scalar.ScalarValue(results.current_page,
'%s_idle_deadline_overrun' % v8_event_stat.result_name, 'ms',
v8_event_stat.idle_task_overrun_duration,
('Total idle task deadline overrun for %s idle tasks'
% v8_event_stat.result_description)))
results.AddValue(scalar.ScalarValue(results.current_page,
'%s_percentage_idle' % v8_event_stat.result_name, '%',
v8_event_stat.percentage_thread_duration_during_idle,
('Percentage of %s spent in idle time' %
v8_event_stat.result_description)))
# Add total metrics.
gc_total = sum(x.thread_duration for x in self._v8_event_stats)
gc_total_outside_idle = sum(
x.thread_duration_outside_idle for x in self._v8_event_stats)
gc_total_idle_deadline_overrun = sum(
x.idle_task_overrun_duration for x in self._v8_event_stats)
gc_total_percentage_idle = statistics.DivideIfPossibleOrZero(
100 * (gc_total - gc_total_outside_idle), gc_total)
results.AddValue(scalar.ScalarValue(results.current_page,
'v8_gc_total', 'ms', gc_total,
'Total thread duration of all garbage collection events'))
results.AddValue(scalar.ScalarValue(results.current_page,
'v8_gc_total_outside_idle', 'ms', gc_total_outside_idle,
'Total thread duration of all garbage collection events outside of '
'idle tasks'))
results.AddValue(scalar.ScalarValue(results.current_page,
'v8_gc_total_idle_deadline_overrun', 'ms',
gc_total_idle_deadline_overrun,
'Total idle task deadline overrun for all idle tasks garbage '
'collection events'))
results.AddValue(scalar.ScalarValue(results.current_page,
'v8_gc_total_percentage_idle', '%', gc_total_percentage_idle,
'Percentage of the thread duration of all garbage collection events '
'spent inside of idle tasks'))
def _AddCpuTimeStatsToResults(self, thread, results):
if thread.toplevel_slices:
start_time = min(s.start for s in thread.toplevel_slices)
end_time = max(s.end for s in thread.toplevel_slices)
duration = end_time - start_time
cpu_time = sum(s.thread_duration for s in thread.toplevel_slices)
else:
duration = cpu_time = 0
results.AddValue(scalar.ScalarValue(
results.current_page, 'duration', 'ms', duration))
results.AddValue(scalar.ScalarValue(
results.current_page, 'cpu_time', 'ms', cpu_time))
def _FindV8EventStatForEvent(v8_event_stats_list, event_name):
for v8_event_stat in v8_event_stats_list:
if v8_event_stat.src_event_name == event_name:
return v8_event_stat
return None
def _ParentIdleTask(event):
parent = event.parent_slice
while parent:
if parent.name == V8GCTimes._IDLE_TASK_PARENT:
return parent
parent = parent.parent_slice
return None
class V8EventStat(object):
def __init__(self, src_event_name, result_name, result_description):
self.src_event_name = src_event_name
self.result_name = result_name
self.result_description = result_description
self.thread_duration = 0.0
self.thread_duration_inside_idle = 0.0
self.idle_task_overrun_duration = 0.0
@property
def thread_duration_outside_idle(self):
return self.thread_duration - self.thread_duration_inside_idle
@property
def percentage_thread_duration_during_idle(self):
return statistics.DivideIfPossibleOrZero(
100 * self.thread_duration_inside_idle, self.thread_duration)
This diff is collapsed.
...@@ -24,7 +24,8 @@ class SpinningBallsPage(page_module.Page): ...@@ -24,7 +24,8 @@ class SpinningBallsPage(page_module.Page):
"document.readyState == 'complete'") "document.readyState == 'complete'")
def RunPageInteractions(self, action_runner): def RunPageInteractions(self, action_runner):
interaction = action_runner.BeginInteraction('RunSmoothAllActions') interaction = action_runner.BeginInteraction(
'RunSmoothAllActions', is_fast=True)
action_runner.Wait(15) action_runner.Wait(15)
interaction.End() interaction.End()
......
...@@ -33,8 +33,8 @@ class ActionRunner(object): ...@@ -33,8 +33,8 @@ class ActionRunner(object):
action.WillRunAction(self._tab) action.WillRunAction(self._tab)
action.RunAction(self._tab) action.RunAction(self._tab)
def BeginInteraction(self, label, is_smooth=False, is_responsive=False, def BeginInteraction(self, label, is_fast=False, is_smooth=False,
repeatable=False): is_responsive=False, repeatable=False):
"""Marks the beginning of an interaction record. """Marks the beginning of an interaction record.
An interaction record is a labeled time period containing An interaction record is a labeled time period containing
...@@ -46,6 +46,8 @@ class ActionRunner(object): ...@@ -46,6 +46,8 @@ class ActionRunner(object):
Args: Args:
label: A label for this particular interaction. This can be any label: A label for this particular interaction. This can be any
user-defined string, but must not contain '/'. user-defined string, but must not contain '/'.
is_fast: Whether to measure how fast the browser completes necessary work
for this interaction record. See fast_metric.py for details.
is_smooth: Whether to check for smoothness metrics for this interaction. is_smooth: Whether to check for smoothness metrics for this interaction.
is_responsive: Whether to check for responsiveness metrics for is_responsive: Whether to check for responsiveness metrics for
this interaction. this interaction.
...@@ -54,6 +56,8 @@ class ActionRunner(object): ...@@ -54,6 +56,8 @@ class ActionRunner(object):
have the same flags. have the same flags.
""" """
flags = [] flags = []
if is_fast:
flags.append(timeline_interaction_record.IS_FAST)
if is_smooth: if is_smooth:
flags.append(timeline_interaction_record.IS_SMOOTH) flags.append(timeline_interaction_record.IS_SMOOTH)
if is_responsive: if is_responsive:
...@@ -65,8 +69,8 @@ class ActionRunner(object): ...@@ -65,8 +69,8 @@ class ActionRunner(object):
interaction.Begin() interaction.Begin()
return interaction return interaction
def BeginGestureInteraction(self, label, is_smooth=False, is_responsive=False, def BeginGestureInteraction(self, label, is_fast=False, is_smooth=False,
repeatable=False): is_responsive=False, repeatable=False):
"""Marks the beginning of a gesture-based interaction record. """Marks the beginning of a gesture-based interaction record.
This is similar to normal interaction record, but it will This is similar to normal interaction record, but it will
...@@ -80,6 +84,8 @@ class ActionRunner(object): ...@@ -80,6 +84,8 @@ class ActionRunner(object):
Args: Args:
label: A label for this particular interaction. This can be any label: A label for this particular interaction. This can be any
user-defined string, but must not contain '/'. user-defined string, but must not contain '/'.
is_fast: Whether to measure how fast the browser completes necessary work
for this interaction record. See fast_metric.py for details.
is_smooth: Whether to check for smoothness metrics for this interaction. is_smooth: Whether to check for smoothness metrics for this interaction.
is_responsive: Whether to check for responsiveness metrics for is_responsive: Whether to check for responsiveness metrics for
this interaction. this interaction.
...@@ -87,8 +93,8 @@ class ActionRunner(object): ...@@ -87,8 +93,8 @@ class ActionRunner(object):
as this interaction. All interactions with the same logical name must as this interaction. All interactions with the same logical name must
have the same flags. have the same flags.
""" """
return self.BeginInteraction('Gesture_' + label, is_smooth, is_responsive, return self.BeginInteraction('Gesture_' + label, is_fast, is_smooth,
repeatable) is_responsive, repeatable)
def NavigateToPage(self, page, timeout_in_seconds=60): def NavigateToPage(self, page, timeout_in_seconds=60):
"""Navigates to the given page. """Navigates to the given page.
......
...@@ -51,9 +51,10 @@ class ActionRunnerInteractionTest(tab_test_case.TabTestCase): ...@@ -51,9 +51,10 @@ class ActionRunnerInteractionTest(tab_test_case.TabTestCase):
# Test disabled for android: crbug.com/437057 # Test disabled for android: crbug.com/437057
@decorators.Disabled('android', 'chromeos') @decorators.Disabled('android', 'chromeos')
def testIssuingMultipleMeasurementInteractionRecords(self): def testIssuingMultipleMeasurementInteractionRecords(self):
self.VerifyIssuingInteractionRecords(is_fast=True)
self.VerifyIssuingInteractionRecords(is_responsive=True) self.VerifyIssuingInteractionRecords(is_responsive=True)
self.VerifyIssuingInteractionRecords(is_smooth=True) self.VerifyIssuingInteractionRecords(is_smooth=True)
self.VerifyIssuingInteractionRecords(is_responsive=True, is_smooth=True) self.VerifyIssuingInteractionRecords(is_fast=True, is_smooth=True)
class ActionRunnerTest(tab_test_case.TabTestCase): class ActionRunnerTest(tab_test_case.TabTestCase):
......
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
from telemetry.timeline import bounds
from telemetry.value import scalar
from telemetry.web_perf import timeline_interaction_record as tir_module
from telemetry.web_perf.metrics import timeline_based_metric
from telemetry.web_perf.metrics import v8_stats as v8_stats_module
class FastMetric(timeline_based_metric.TimelineBasedMetric):
def __init__(self):
super(FastMetric, self).__init__()
def AddResults(self, model, renderer_thread, interaction_records, results):
"""Add 11 results: duration, cpu_time, and idle_time,
incremental_marking, incremental_marking_outside_idle,
scavenger, scavenger_outside_idle,
mark_compactor, mark_compactor_outside_idle,
total_garbage_collection,
total_garbage_collection_outside_idle
duration is the total wall time for |interaction_records|.
cpu_time is the renderer thread time that intersects |interaction_records|.
idle time is wall time for |interaction_records| for which renderer slices
do not overlap. Note that unscheduled renderer thread time is not
counted. Idle time is time for which there was nothing to do.
incremental_marking is the total thread duration spent in incremental
marking steps.
incremental_marking_outside_idle is the thread duration spent in incremental
marking steps outside of idle notifications.
scavenger is the total thread duration spent in scavenges.
scavenger_outside_idle is the thread duration spent in scavenges outside of
idle notifications.
mark_compactor is the total thread duration spent in mark-sweep-compactor.
mark_compactor_outside_idle is the thread duration spent in
mark-sweep-compactor outside of idle notifications.
total_garbage_collection is the total thread duration spend in garbage
collection
total_garbage_collection_outside_idle is the total thread duration spend in
garbage collection outside of idle notification.
Args:
model: a TimelineModule instance
renderer_thread: a telemetry.timeline.thread.Thread() instance
interaction_records: an iterable of TimelineInteractionRecord instances
results: an instance of page.PageTestResults
"""
self.VerifyNonOverlappedRecords(interaction_records)
duration = sum(r.end - r.start for r in interaction_records)
results.AddValue(scalar.ScalarValue(
results.current_page, 'fast-duration', 'ms', duration))
try:
cpu_time = sum(
r.GetOverlappedThreadTimeForSlice(s)
for r in interaction_records
for s in renderer_thread.toplevel_slices)
except tir_module.NoThreadTimeDataException:
logging.warning(
'Main thread cpu_time cannot be computed for records %s since '
'trace does not contain thread time data.',
repr(interaction_records))
else:
results.AddValue(scalar.ScalarValue(
results.current_page, 'fast-cpu_time', 'ms', cpu_time))
idle_time = duration - sum(
bounds.Bounds.GetOverlap(r.start, r.end, s.start, s.end)
for r in interaction_records
for s in renderer_thread.toplevel_slices)
results.AddValue(scalar.ScalarValue(
results.current_page, 'fast-idle_time', 'ms', idle_time))
v8_stats = v8_stats_module.V8Stats(renderer_thread, interaction_records)
for event_stats in v8_stats.all_event_stats:
results.AddValue(scalar.ScalarValue(
results.current_page, 'fast-' + event_stats.result_name, 'ms',
event_stats.thread_duration,
event_stats.result_description))
results.AddValue(scalar.ScalarValue(
results.current_page,
'fast-' + event_stats.result_name + '_outside_idle', 'ms',
event_stats.thread_duration_outside_idle,
event_stats.result_description + 'outside of idle notifications'))
results.AddValue(scalar.ScalarValue(
results.current_page, 'fast-total_garbage_collection', 'ms',
v8_stats.total_gc_thread_duration,
'Total thread duration of all garbage collection events'))
results.AddValue(scalar.ScalarValue(
results.current_page, 'fast-total_garbage_collection_outside_idle',
'ms', v8_stats.total_gc_thread_duration_outside_idle,
'Total thread duration of all garbage collection events outside of idle'
'notifications'))
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import StringIO
import unittest
from telemetry.results import page_test_results
from telemetry.timeline import model as model_module
from telemetry.timeline import async_slice
from telemetry.web_perf import timeline_interaction_record as tir_module
from telemetry.web_perf.metrics import fast_metric
class RendererThreadHelper(object):
def __init__(self, wall_start, wall_duration, thread_start, thread_duration):
self._model = model_module.TimelineModel()
renderer_process = self._model.GetOrCreateProcess(1)
self._renderer_thread = renderer_process.GetOrCreateThread(2)
self._renderer_thread.name = 'CrRendererMain'
self._renderer_thread.BeginSlice('cat1', 'x.y', wall_start, thread_start)
self._renderer_thread.EndSlice(wall_start + wall_duration,
thread_start + thread_duration)
self._async_slices = []
def AddInteraction(self, logical_name='LogicalName', **kwargs):
# Rename kwargs for AsyncSlice.
kwargs['timestamp'] = kwargs.pop('wall_start')
kwargs['duration'] = kwargs.pop('wall_duration')
self._async_slices.append(async_slice.AsyncSlice(
'cat', 'Interaction.%s/is_fast' % logical_name,
start_thread=self._renderer_thread, end_thread=self._renderer_thread,
**kwargs))
def AddEvent(self, category, name, wall_start, wall_duration, thread_start,
thread_duration):
self._renderer_thread.BeginSlice(category, name, wall_start, thread_start)
self._renderer_thread.EndSlice(wall_start + wall_duration,
thread_start + thread_duration)
def MeasureFakePage(self, metric):
self._renderer_thread.async_slices.extend(self._async_slices)
self._model.FinalizeImport()
interaction_records = [
tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
for s in self._async_slices]
results = page_test_results.PageTestResults()
fake_page = None
results.WillRunPage(fake_page)
metric.AddResults(self._model, self._renderer_thread, interaction_records,
results)
results.DidRunPage(fake_page)
return results
class FastMetricTests(unittest.TestCase):
def setUp(self):
self.log_output = StringIO.StringIO()
self.stream_handler = logging.StreamHandler(self.log_output)
logging.getLogger().addHandler(self.stream_handler)
def tearDown(self):
logging.getLogger().removeHandler(self.stream_handler)
self.log_output.close()
def LogOutput(self):
return self.log_output.getvalue()
def ActualValues(self, results):
return sorted(
(v.name, v.units, v.value)
for v in results.all_page_specific_values
)
def testAddResultsWithThreadTime(self):
# Wall time diagram:
# 1 2 3 4
# 01234567890123456789012345678901234567890123456789
# [ x.y ]
# [ Interaction.LogicalName/is_fast ]
renderer_thread_helper = RendererThreadHelper(
wall_start=5, wall_duration=35, thread_start=0, thread_duration=54)
renderer_thread_helper.AddInteraction(
wall_start=32, wall_duration=37, thread_start=51, thread_duration=33)
metric = fast_metric.FastMetric()
results = renderer_thread_helper.MeasureFakePage(metric)
expected_values = [
('fast-cpu_time', 'ms', 3), # 54 - 51; thread overlap
('fast-duration', 'ms', 37), # total interaction wall duration
('fast-idle_time', 'ms', 29), # (32 + 37) - (5 + 35); interaction wall
# time outside of renderer wall time.
('fast-incremental_marking', 'ms', 0.0),
('fast-incremental_marking_outside_idle', 'ms', 0.0),
('fast-mark_compactor', 'ms', 0.0),
('fast-mark_compactor_outside_idle', 'ms', 0.0),
('fast-scavenger', 'ms', 0.0),
('fast-scavenger_outside_idle', 'ms', 0.0),
('fast-total_garbage_collection', 'ms', 0.0),
('fast-total_garbage_collection_outside_idle', 'ms', 0.0)
]
self.assertEqual(expected_values, self.ActualValues(results))
def testAddResultsWithoutThreadTime(self):
# Wall time diagram:
# 1 2 3 4
# 01234567890123456789012345678901234567890123456789
# [ x.y ]
# [ Interaction.LogicalName/is_fast ]
renderer_thread_helper = RendererThreadHelper(
wall_start=5, wall_duration=35, thread_start=0, thread_duration=54)
renderer_thread_helper.AddInteraction(
wall_start=32, wall_duration=37) # no thread_start, no thread_duration
metric = fast_metric.FastMetric()
results = renderer_thread_helper.MeasureFakePage(metric)
expected_values = [
# cpu_time is skipped because there is no thread time.
('fast-duration', 'ms', 37), # total interaction wall duration
('fast-idle_time', 'ms', 29), # (32 + 37) - (5 + 35); interaction wall
# time outside of renderer wall time.
('fast-incremental_marking', 'ms', 0.0),
('fast-incremental_marking_outside_idle', 'ms', 0.0),
('fast-mark_compactor', 'ms', 0.0),
('fast-mark_compactor_outside_idle', 'ms', 0.0),
('fast-scavenger', 'ms', 0.0),
('fast-scavenger_outside_idle', 'ms', 0.0),
('fast-total_garbage_collection', 'ms', 0.0),
('fast-total_garbage_collection_outside_idle', 'ms', 0.0)
]
self.assertEqual(expected_values, self.ActualValues(results))
self.assertIn('Main thread cpu_time cannot be computed for records',
self.LogOutput())
def testAddResultsWithMultipleInteractions(self):
# Wall time diagram:
# 1 2 3 4
# 01234567890123456789012345678901234567890123456789
# [ x.y ]
# [ Interaction.Foo/is_fast ] [ Interaction.Bar/is_fast ]
renderer_thread_helper = RendererThreadHelper(
wall_start=2, wall_duration=45, thread_start=0, thread_duration=101)
renderer_thread_helper.AddInteraction(
logical_name='Foo',
wall_start=6, wall_duration=27, thread_start=51, thread_duration=33)
renderer_thread_helper.AddInteraction(
logical_name='Bar',
wall_start=38, wall_duration=27, thread_start=90, thread_duration=33)
metric = fast_metric.FastMetric()
results = renderer_thread_helper.MeasureFakePage(metric)
expected_values = [
('fast-cpu_time', 'ms', 44), # thread overlap
('fast-duration', 'ms', 54), # 27 + 27; total interaction wall duration
('fast-idle_time', 'ms', 18), # (38 + 27) - (2 + 45); interaction wall
# time outside of renderer wall time.
('fast-incremental_marking', 'ms', 0.0),
('fast-incremental_marking_outside_idle', 'ms', 0.0),
('fast-mark_compactor', 'ms', 0.0),
('fast-mark_compactor_outside_idle', 'ms', 0.0),
('fast-scavenger', 'ms', 0.0),
('fast-scavenger_outside_idle', 'ms', 0.0),
('fast-total_garbage_collection', 'ms', 0.0),
('fast-total_garbage_collection_outside_idle', 'ms', 0.0)
]
self.assertEqual(expected_values, self.ActualValues(results))
def testAddResultsWithGarbeCollectionEvents(self):
# Thread time diagram:
# 1 2 3 4 5
# 012345678901234567890123456789012345678901234567890123456789
# [ x.y ]
# [ Interaction.Foo/is_fast ]
# [ Idle ] [Idle] [Idle]
# [ S ] [S] [ I ] [I] [MC ] [MC]
renderer_thread_helper = RendererThreadHelper(
wall_start=1, wall_duration=57, thread_start=1, thread_duration=57)
renderer_thread_helper.AddInteraction(
logical_name='Foo',
wall_start=3, wall_duration=58, thread_start=3, thread_duration=58)
renderer_thread_helper.AddEvent('v8', 'V8.GCIdleNotification', 3, 7, 3, 7)
renderer_thread_helper.AddEvent('v8', 'V8.GCIdleNotification', 22, 5, 22, 5)
renderer_thread_helper.AddEvent('v8', 'V8.GCIdleNotification', 41, 5, 41, 5)
renderer_thread_helper.AddEvent('v8', 'V8.GCScavenger', 5, 4, 5, 4)
renderer_thread_helper.AddEvent('v8', 'V8.GCScavenger', 15, 2, 15, 2)
renderer_thread_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 23, 4, 23,
4)
renderer_thread_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 34, 2, 34,
2)
renderer_thread_helper.AddEvent('v8', 'V8.GCCompactor', 42, 4, 42, 4)
renderer_thread_helper.AddEvent('v8', 'V8.GCCompactor', 52, 3, 52, 3)
metric = fast_metric.FastMetric()
results = renderer_thread_helper.MeasureFakePage(metric)
expected_values = [
('fast-cpu_time', 'ms', 55), # thread overlap
('fast-duration', 'ms', 58), # total interaction wall duration
('fast-idle_time', 'ms', 3), # (3 + 58) - (1 + 57); interaction wall
# time outside of renderer wall time.
('fast-incremental_marking', 'ms', 6.0),
('fast-incremental_marking_outside_idle', 'ms', 2.0),
('fast-mark_compactor', 'ms', 7.0),
('fast-mark_compactor_outside_idle', 'ms', 3.0),
('fast-scavenger', 'ms', 6.0),
('fast-scavenger_outside_idle', 'ms', 2.0),
('fast-total_garbage_collection', 'ms', 19.0),
('fast-total_garbage_collection_outside_idle', 'ms', 7.0)
]
self.assertEqual(expected_values, self.ActualValues(results))
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
class EventStats(object):
def __init__(self, src_event_name, result_name, result_description):
self.src_event_name = src_event_name
self.result_name = result_name
self.result_description = result_description
self.thread_duration = 0.0
self.thread_duration_inside_idle = 0.0
@property
def thread_duration_outside_idle(self):
return self.thread_duration - self.thread_duration_inside_idle
def _FindEventStats(event_stats_list, event_name):
for event_stats in event_stats_list:
if event_stats.src_event_name == event_name:
return event_stats
return None
def _IsDescendentOfIdleNotification(event):
parent = event.parent_slice
while parent:
if parent.name == 'V8.GCIdleNotification':
return True
parent = parent.parent_slice
return False
class V8Stats(object):
def __init__(self, renderer_thread, interaction_records):
self.all_event_stats = [
EventStats('V8.GCIncrementalMarking',
'incremental_marking',
'total thread duration spent in incremental marking steps'),
EventStats('V8.GCScavenger',
'scavenger',
'total thread duration spent in scavenges'),
EventStats('V8.GCCompactor',
'mark_compactor',
'total thread duration spent in mark-sweep-compactor')]
# Find all GC events contained in an interaction record
for event in renderer_thread.IterAllSlices():
event_stats = _FindEventStats(self.all_event_stats, event.name)
if not event_stats:
continue
for r in interaction_records:
if not r.GetBounds().ContainsInterval(event.start, event.end):
continue
event_stats.thread_duration += event.thread_duration
if _IsDescendentOfIdleNotification(event):
event_stats.thread_duration_inside_idle += event.thread_duration
@property
def total_gc_thread_duration(self):
return sum(x.thread_duration for x in self.all_event_stats)
@property
def total_gc_thread_duration_outside_idle(self):
return sum(x.thread_duration_outside_idle for x in self.all_event_stats)
...@@ -10,6 +10,7 @@ from telemetry.page import page_test ...@@ -10,6 +10,7 @@ from telemetry.page import page_test
from telemetry.timeline import model as model_module from telemetry.timeline import model as model_module
from telemetry.value import trace from telemetry.value import trace
from telemetry.web_perf import timeline_interaction_record as tir_module from telemetry.web_perf import timeline_interaction_record as tir_module
from telemetry.web_perf.metrics import fast_metric
from telemetry.web_perf.metrics import responsiveness_metric from telemetry.web_perf.metrics import responsiveness_metric
from telemetry.web_perf.metrics import smoothness from telemetry.web_perf.metrics import smoothness
...@@ -18,16 +19,19 @@ from telemetry.web_perf.metrics import smoothness ...@@ -18,16 +19,19 @@ from telemetry.web_perf.metrics import smoothness
# overhead increases. The user of the measurement must therefore chose between # overhead increases. The user of the measurement must therefore chose between
# a few levels of instrumentation. # a few levels of instrumentation.
NO_OVERHEAD_LEVEL = 'no-overhead' NO_OVERHEAD_LEVEL = 'no-overhead'
V8_OVERHEAD_LEVEL = 'v8-overhead'
MINIMAL_OVERHEAD_LEVEL = 'minimal-overhead' MINIMAL_OVERHEAD_LEVEL = 'minimal-overhead'
DEBUG_OVERHEAD_LEVEL = 'debug-overhead' DEBUG_OVERHEAD_LEVEL = 'debug-overhead'
ALL_OVERHEAD_LEVELS = [ ALL_OVERHEAD_LEVELS = [
NO_OVERHEAD_LEVEL, NO_OVERHEAD_LEVEL,
V8_OVERHEAD_LEVEL,
MINIMAL_OVERHEAD_LEVEL, MINIMAL_OVERHEAD_LEVEL,
DEBUG_OVERHEAD_LEVEL DEBUG_OVERHEAD_LEVEL
] ]
DEFAULT_METRICS = { DEFAULT_METRICS = {
tir_module.IS_FAST: fast_metric.FastMetric,
tir_module.IS_SMOOTH: smoothness.SmoothnessMetric, tir_module.IS_SMOOTH: smoothness.SmoothnessMetric,
tir_module.IS_RESPONSIVE: responsiveness_metric.ResponsivenessMetric, tir_module.IS_RESPONSIVE: responsiveness_metric.ResponsivenessMetric,
} }
...@@ -138,8 +142,9 @@ class Options(object): ...@@ -138,8 +142,9 @@ class Options(object):
and the tracing is filtered accordingly. and the tracing is filtered accordingly.
overhead_level: Can either be a custom TracingCategoryFilter object or overhead_level: Can either be a custom TracingCategoryFilter object or
one of NO_OVERHEAD_LEVEL, MINIMAL_OVERHEAD_LEVEL or one of NO_OVERHEAD_LEVEL, V8_OVERHEAD_LEVEL, MINIMAL_OVERHEAD_LEVEL,
DEBUG_OVERHEAD_LEVEL. or DEBUG_OVERHEAD_LEVEL. The v8 overhead level is a temporary solution
that may be removed.
get_metrics_from_flags_callback: Callback function which returns a get_metrics_from_flags_callback: Callback function which returns a
a list of metrics based on timeline record flags. See the default a list of metrics based on timeline record flags. See the default
_GetMetricsFromFlags() as an example. _GetMetricsFromFlags() as an example.
...@@ -219,6 +224,11 @@ class TimelineBasedMeasurement(object): ...@@ -219,6 +224,11 @@ class TimelineBasedMeasurement(object):
if self._tbm_options.overhead_level == NO_OVERHEAD_LEVEL: if self._tbm_options.overhead_level == NO_OVERHEAD_LEVEL:
category_filter = tracing_category_filter.CreateNoOverheadFilter() category_filter = tracing_category_filter.CreateNoOverheadFilter()
# TODO(ernstm): Remove this overhead level when benchmark relevant v8
# events become available in the 'benchmark' category.
elif self._tbm_options.overhead_level == V8_OVERHEAD_LEVEL:
category_filter = tracing_category_filter.CreateNoOverheadFilter()
category_filter.AddIncludedCategory('v8')
elif self._tbm_options.overhead_level == MINIMAL_OVERHEAD_LEVEL: elif self._tbm_options.overhead_level == MINIMAL_OVERHEAD_LEVEL:
category_filter = tracing_category_filter.CreateMinimalOverheadFilter() category_filter = tracing_category_filter.CreateMinimalOverheadFilter()
else: else:
......
...@@ -22,6 +22,16 @@ from telemetry.web_perf import timeline_interaction_record as tir_module ...@@ -22,6 +22,16 @@ from telemetry.web_perf import timeline_interaction_record as tir_module
from telemetry.web_perf.metrics import timeline_based_metric from telemetry.web_perf.metrics import timeline_based_metric
class FakeFastMetric(timeline_based_metric.TimelineBasedMetric):
def AddResults(self, model, renderer_thread, interaction_records, results):
results.AddValue(scalar.ScalarValue(
results.current_page, 'FakeFastMetric', 'ms', 1))
results.AddValue(scalar.ScalarValue(
results.current_page, 'FastMetricRecords', 'count',
len(interaction_records)))
class FakeSmoothMetric(timeline_based_metric.TimelineBasedMetric): class FakeSmoothMetric(timeline_based_metric.TimelineBasedMetric):
def AddResults(self, model, renderer_thread, interaction_records, results): def AddResults(self, model, renderer_thread, interaction_records, results):
...@@ -43,6 +53,7 @@ class FakeLoadingMetric(timeline_based_metric.TimelineBasedMetric): ...@@ -43,6 +53,7 @@ class FakeLoadingMetric(timeline_based_metric.TimelineBasedMetric):
FAKE_METRICS_METRICS = { FAKE_METRICS_METRICS = {
tir_module.IS_FAST: FakeFastMetric,
tir_module.IS_SMOOTH: FakeSmoothMetric, tir_module.IS_SMOOTH: FakeSmoothMetric,
tir_module.IS_RESPONSIVE: FakeLoadingMetric, tir_module.IS_RESPONSIVE: FakeLoadingMetric,
} }
...@@ -126,7 +137,7 @@ class TimelineBasedMetricsTests(unittest.TestCase): ...@@ -126,7 +137,7 @@ class TimelineBasedMetricsTests(unittest.TestCase):
d.AddInteraction(d.renderer_thread, ts=25, duration=5, d.AddInteraction(d.renderer_thread, ts=25, duration=5,
marker='Interaction.LogicalName2/is_responsive') marker='Interaction.LogicalName2/is_responsive')
d.AddInteraction(d.foo_thread, ts=50, duration=15, d.AddInteraction(d.foo_thread, ts=50, duration=15,
marker='Interaction.LogicalName3/is_smooth') marker='Interaction.LogicalName3/is_fast')
d.FinalizeImport() d.FinalizeImport()
self.assertEquals(2, len(d.threads_to_records_map)) self.assertEquals(2, len(d.threads_to_records_map))
...@@ -147,7 +158,7 @@ class TimelineBasedMetricsTests(unittest.TestCase): ...@@ -147,7 +158,7 @@ class TimelineBasedMetricsTests(unittest.TestCase):
self.assertIn(d.foo_thread, d.threads_to_records_map) self.assertIn(d.foo_thread, d.threads_to_records_map)
interactions = d.threads_to_records_map[d.foo_thread] interactions = d.threads_to_records_map[d.foo_thread]
self.assertEquals(1, len(interactions)) self.assertEquals(1, len(interactions))
self.assertTrue(interactions[0].is_smooth) self.assertTrue(interactions[0].is_fast)
self.assertEquals(50, interactions[0].start) self.assertEquals(50, interactions[0].start)
self.assertEquals(65, interactions[0].end) self.assertEquals(65, interactions[0].end)
...@@ -157,12 +168,16 @@ class TimelineBasedMetricsTests(unittest.TestCase): ...@@ -157,12 +168,16 @@ class TimelineBasedMetricsTests(unittest.TestCase):
marker='Interaction.LogicalName1/is_smooth') marker='Interaction.LogicalName1/is_smooth')
d.AddInteraction(d.foo_thread, ts=25, duration=5, d.AddInteraction(d.foo_thread, ts=25, duration=5,
marker='Interaction.LogicalName2/is_responsive') marker='Interaction.LogicalName2/is_responsive')
d.AddInteraction(d.renderer_thread, ts=50, duration=15,
marker='Interaction.LogicalName3/is_fast')
d.FinalizeImport() d.FinalizeImport()
d.AddResults() d.AddResults()
self.assertEquals(1, len(d.results.FindAllPageSpecificValuesNamed( self.assertEquals(1, len(d.results.FindAllPageSpecificValuesNamed(
'LogicalName1-FakeSmoothMetric'))) 'LogicalName1-FakeSmoothMetric')))
self.assertEquals(1, len(d.results.FindAllPageSpecificValuesNamed( self.assertEquals(1, len(d.results.FindAllPageSpecificValuesNamed(
'LogicalName2-FakeLoadingMetric'))) 'LogicalName2-FakeLoadingMetric')))
self.assertEquals(1, len(d.results.FindAllPageSpecificValuesNamed(
'LogicalName3-FakeFastMetric')))
def testDuplicateInteractionsInDifferentThreads(self): def testDuplicateInteractionsInDifferentThreads(self):
d = TimelineBasedMetricTestData() d = TimelineBasedMetricTestData()
...@@ -262,6 +277,44 @@ class TimelineBasedMeasurementTest(page_test_test_case.PageTestTestCase): ...@@ -262,6 +277,44 @@ class TimelineBasedMeasurementTest(page_test_test_case.PageTestTestCase):
'DrawerAnimation-frame_time_discrepancy') 'DrawerAnimation-frame_time_discrepancy')
self.assertEquals(len(v), 1) self.assertEquals(len(v), 1)
# This test is flaky when run in parallel on the mac: crbug.com/426676
# Also, fails on android: crbug.com/437057
@decorators.Disabled('android', 'mac')
def testFastTimelineBasedMeasurementForSmoke(self):
ps = self.CreateEmptyPageSet()
ps.AddUserStory(TestTimelinebasedMeasurementPage(
ps, ps.base_dir, trigger_slow=True))
tbm = tbm_module.TimelineBasedMeasurement(tbm_module.Options())
measurement = tbm_module.TimelineBasedPageTest(tbm)
results = self.RunMeasurement(measurement, ps, options=self._options)
self.assertEquals([], results.failures)
expected_names = set([
'SlowThreadJsRun-fast-duration',
'SlowThreadJsRun-fast-idle_time',
'SlowThreadJsRun-fast-incremental_marking',
'SlowThreadJsRun-fast-incremental_marking_outside_idle',
'SlowThreadJsRun-fast-mark_compactor',
'SlowThreadJsRun-fast-mark_compactor_outside_idle',
'SlowThreadJsRun-fast-scavenger',
'SlowThreadJsRun-fast-scavenger_outside_idle',
'SlowThreadJsRun-fast-total_garbage_collection',
'SlowThreadJsRun-fast-total_garbage_collection_outside_idle',
'trace',
])
if platform.GetHostPlatform().GetOSName() != 'win':
# CPU metric is only supported non-Windows platforms.
expected_names.add('SlowThreadJsRun-fast-cpu_time')
self.assertEquals(
expected_names, set(v.name for v in results.all_page_specific_values))
# In interaction_enabled_page.html, the "slow" interaction executes
# a loop with window.performance.now() to wait 200ms.
# fast-duration measures wall time so its value should be at least 200ms.
v = results.FindAllPageSpecificValuesNamed('SlowThreadJsRun-fast-duration')
self.assertGreaterEqual(v[0].value, 200.0)
# Disabled since mainthread_jank metric is not supported on windows platform. # Disabled since mainthread_jank metric is not supported on windows platform.
# Also, flaky on the mac when run in parallel: crbug.com/426676 # Also, flaky on the mac when run in parallel: crbug.com/426676
# Also, fails on android: crbug.com/437057 # Also, fails on android: crbug.com/437057
......
...@@ -7,6 +7,8 @@ import re ...@@ -7,6 +7,8 @@ import re
import telemetry.timeline.bounds as timeline_bounds import telemetry.timeline.bounds as timeline_bounds
from telemetry import decorators from telemetry import decorators
# Enables the fast metric for this interaction
IS_FAST = 'is_fast'
# Enables the responsiveness metric for this interaction # Enables the responsiveness metric for this interaction
IS_RESPONSIVE = 'is_responsive' IS_RESPONSIVE = 'is_responsive'
# Enables the smoothness metric for this interaction # Enables the smoothness metric for this interaction
...@@ -15,6 +17,7 @@ IS_SMOOTH = 'is_smooth' ...@@ -15,6 +17,7 @@ IS_SMOOTH = 'is_smooth'
REPEATABLE = 'repeatable' REPEATABLE = 'repeatable'
METRICS = [ METRICS = [
IS_FAST,
IS_RESPONSIVE, IS_RESPONSIVE,
IS_SMOOTH IS_SMOOTH
] ]
...@@ -85,7 +88,7 @@ class TimelineInteractionRecord(object): ...@@ -85,7 +88,7 @@ class TimelineInteractionRecord(object):
is currently done by pushing markers into the console.time/timeEnd API: this is currently done by pushing markers into the console.time/timeEnd API: this
for instance can be issued in JS: for instance can be issued in JS:
var str = 'Interaction.SendEmail/is_smooth,is_responsive'; var str = 'Interaction.SendEmail/is_smooth,is_responsive,is_fast';
console.time(str); console.time(str);
setTimeout(function() { setTimeout(function() {
console.timeEnd(str); console.timeEnd(str);
...@@ -93,10 +96,11 @@ class TimelineInteractionRecord(object): ...@@ -93,10 +96,11 @@ class TimelineInteractionRecord(object):
When run with perf.measurements.timeline_based_measurement running, this will When run with perf.measurements.timeline_based_measurement running, this will
then cause a TimelineInteractionRecord to be created for this range with then cause a TimelineInteractionRecord to be created for this range with
smoothness and responsive metrics reported for the marked up 1000ms smoothness, responsive, and fast metrics reported for the marked up 1000ms
time-range. time-range.
The valid interaction flags are: The valid interaction flags are:
* is_fast: Enables the fast metric
* is_responsive: Enables the responsiveness metric * is_responsive: Enables the responsiveness metric
* is_smooth: Enables the smoothness metric * is_smooth: Enables the smoothness metric
* repeatable: Allows other interactions to use the same label * repeatable: Allows other interactions to use the same label
...@@ -123,6 +127,10 @@ class TimelineInteractionRecord(object): ...@@ -123,6 +127,10 @@ class TimelineInteractionRecord(object):
def end(self): def end(self):
return self._end return self._end
@property
def is_fast(self):
return IS_FAST in self._flags
@property @property
def is_responsive(self): def is_responsive(self):
return IS_RESPONSIVE in self._flags return IS_RESPONSIVE in self._flags
......
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