1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
# 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.util import statistics
from telemetry.value import improvement_direction
from telemetry.value import scalar
from telemetry.web_perf.metrics import timeline_based_metric
import logging
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
self.max_thread_duration = 0.0
self.count = 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)
class V8GCLatency(timeline_based_metric.TimelineBasedMetric):
_RENDERER_MAIN_THREAD = 'CrRendererMain'
_IDLE_TASK_PARENT = 'SingleThreadIdleTaskRunner::RunTask'
def __init__(self):
super(V8GCLatency, self).__init__()
def AddResults(self, model, renderer_thread, interaction_records, results):
self.VerifyNonOverlappedRecords(interaction_records)
self._AddV8MetricsToResults(model, interaction_records, results)
def _AddV8MetricsToResults(self, model,
interaction_records, results):
self._AddV8EventStatsToResults(model, interaction_records, results)
def _AddV8EventStatsToResults(self, model, interactions, results):
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'),
V8EventStat('V8.GCFinalizeMC',
'v8_gc_finalize_incremental',
'finalization of incremental marking'),
V8EventStat('V8.GCFinalizeMCReduceMemory',
'v8_gc_finalize_incremental_reduce_memory',
'finalization of incremental marking with memory reducer')]
label = interactions[0].label
name_to_v8_stat = {x.src_event_name : x for x in v8_event_stats}
thread_time_not_available = False
for event in model.IterAllSlices():
if (not timeline_based_metric.IsEventInInteractions(event, interactions)
or not event.name in name_to_v8_stat):
continue
event_stat = name_to_v8_stat[event.name]
if event.thread_duration is None:
thread_time_not_available = True
event_duration = event.duration
else:
event_duration = event.thread_duration
event_stat.thread_duration += event_duration
event_stat.max_thread_duration = max(event_stat.max_thread_duration,
event_duration)
event_stat.count += 1
parent_idle_task = self._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_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
if thread_time_not_available:
logging.warning(
'thread time is not available in trace data, switch to walltime')
for v8_event_stat in v8_event_stats:
results.AddValue(scalar.ScalarValue(
results.current_page, v8_event_stat.result_name, 'ms',
v8_event_stat.thread_duration,
description=('Total thread duration spent in %s' %
v8_event_stat.result_description),
tir_label=label,
improvement_direction=improvement_direction.DOWN))
results.AddValue(scalar.ScalarValue(
results.current_page, '%s_max' % v8_event_stat.result_name, 'ms',
v8_event_stat.max_thread_duration,
description=('Max thread duration spent in %s' %
v8_event_stat.result_description),
tir_label=label))
results.AddValue(scalar.ScalarValue(
results.current_page, '%s_count' % v8_event_stat.result_name, 'count',
v8_event_stat.count,
description=('Number of %s' %
v8_event_stat.result_description),
tir_label=label,
improvement_direction=improvement_direction.DOWN))
average_thread_duration = statistics.DivideIfPossibleOrZero(
v8_event_stat.thread_duration, v8_event_stat.count)
results.AddValue(scalar.ScalarValue(
results.current_page, '%s_average' % v8_event_stat.result_name, 'ms',
average_thread_duration,
description=('Average thread duration spent in %s' %
v8_event_stat.result_description),
tir_label=label,
improvement_direction=improvement_direction.DOWN))
results.AddValue(scalar.ScalarValue(results.current_page,
'%s_outside_idle' % v8_event_stat.result_name, 'ms',
v8_event_stat.thread_duration_outside_idle,
description=(
'Total thread duration spent in %s outside of idle tasks' %
v8_event_stat.result_description),
tir_label=label))
results.AddValue(scalar.ScalarValue(results.current_page,
'%s_idle_deadline_overrun' % v8_event_stat.result_name, 'ms',
v8_event_stat.idle_task_overrun_duration,
description=('Total idle task deadline overrun for %s idle tasks'
% v8_event_stat.result_description),
tir_label=label,
improvement_direction=improvement_direction.DOWN))
results.AddValue(scalar.ScalarValue(results.current_page,
'%s_percentage_idle' % v8_event_stat.result_name, 'idle%',
v8_event_stat.percentage_thread_duration_during_idle,
description=('Percentage of %s spent in idle time' %
v8_event_stat.result_description),
tir_label=label,
improvement_direction=improvement_direction.UP))
# Add total metrics.
gc_total = sum(x.thread_duration for x in v8_event_stats)
gc_total_outside_idle = sum(
x.thread_duration_outside_idle for x in v8_event_stats)
gc_total_idle_deadline_overrun = sum(
x.idle_task_overrun_duration for x in 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,
description='Total thread duration of all garbage collection events',
tir_label=label,
improvement_direction=improvement_direction.DOWN))
results.AddValue(scalar.ScalarValue(results.current_page,
'v8_gc_total_outside_idle', 'ms', gc_total_outside_idle,
description=(
'Total thread duration of all garbage collection events outside of '
'idle tasks'),
tir_label=label,
improvement_direction=improvement_direction.DOWN))
results.AddValue(scalar.ScalarValue(results.current_page,
'v8_gc_total_idle_deadline_overrun', 'ms',
gc_total_idle_deadline_overrun,
description=(
'Total idle task deadline overrun for all idle tasks garbage '
'collection events'),
tir_label=label,
improvement_direction=improvement_direction.DOWN))
results.AddValue(scalar.ScalarValue(results.current_page,
'v8_gc_total_percentage_idle', 'idle%', gc_total_percentage_idle,
description=(
'Percentage of the thread duration of all garbage collection '
'events spent inside of idle tasks'),
tir_label=label,
improvement_direction=improvement_direction.UP))
def _ParentIdleTask(self, event):
parent = event.parent_slice
while parent:
if parent.name == self._IDLE_TASK_PARENT:
return parent
parent = parent.parent_slice
return None
|