1<!DOCTYPE html>
2<!--
3Copyright (c) 2015 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/base/statistics.html">
9<link rel="import" href="/tracing/metrics/metric_registry.html">
10<link rel="import"
11    href="/tracing/metrics/system_health/animation_smoothness_metric.html">
12<link rel="import"
13    href="/tracing/metrics/system_health/animation_throughput_metric.html">
14<link rel="import" href="/tracing/metrics/system_health/utils.html">
15<link rel="import" href="/tracing/model/user_model/animation_expectation.html">
16<link rel="import" href="/tracing/model/user_model/load_expectation.html">
17<link rel="import" href="/tracing/model/user_model/response_expectation.html">
18<link rel="import" href="/tracing/value/numeric.html">
19<link rel="import" href="/tracing/value/value.html">
20
21<script>
22'use strict';
23
24tr.exportTo('tr.metrics.sh', function() {
25  // In the case of Response, Load, and DiscreteAnimation IRs, Responsiveness is
26  // derived from the time between when the user thinks they begin an interation
27  // (expectedStart) and the time when the screen first changes to reflect the
28  // interaction (actualEnd).  There may be a delay between expectedStart and
29  // when chrome first starts processing the interaction (actualStart) if the
30  // main thread is busy.  The user doesn't know when actualStart is, they only
31  // know when expectedStart is. User responsiveness, by definition, considers
32  // only what the user experiences, so "duration" is defined as actualEnd -
33  // expectedStart.
34
35  // This histogram represents the number of people who we believe would
36  // score the responsiveness at a certain value. We have set this with
37  // just a best-effort guess, though. In #1696, we plan to derive this
38  // experimentally.
39  var RESPONSE_HISTOGRAM = tr.v.Numeric.fromDict({
40    unit: 'unitless',
41    min: 150,
42    max: 5000,
43    centralBinWidth: 485,
44    underflowBin: {min: -Number.MAX_VALUE, max: 150, count: 1000},
45    centralBins: [
46      {min: 150, max: 635, count: 708},
47      {min: 635, max: 1120, count: 223},
48      {min: 1120, max: 1605, count: 50},
49      {min: 1605, max: 2090, count: 33},
50      {min: 2090, max: 2575, count: 23},
51      {min: 2575, max: 3060, count: 17},
52      {min: 3060, max: 3545, count: 12},
53      {min: 3545, max: 4030, count: 8},
54      {min: 4030, max: 4515, count: 4},
55      {min: 4515, max: 5000, count: 1}
56    ],
57    overflowBin: {min: 5000, max: Number.MAX_VALUE, count: 0}
58  });
59
60  var FAST_RESPONSE_HISTOGRAM = tr.v.Numeric.fromDict({
61    unit: 'unitless',
62    min: 66,
63    max: 2200,
64    centralBinWidth: 214,
65    underflowBin: {min: -Number.MAX_VALUE, max: 66, count: 1000},
66    centralBins: [
67      {min: 66, max: 280, count: 708},
68      {min: 280, max: 493, count: 223},
69      {min: 493, max: 706, count: 50},
70      {min: 706, max: 920, count: 33},
71      {min: 920, max: 1133, count: 23},
72      {min: 1133, max: 1346, count: 17},
73      {min: 1346, max: 1560, count: 12},
74      {min: 1560, max: 1773, count: 8},
75      {min: 1773, max: 1987, count: 4},
76      {min: 1987, max: 2200, count: 1}
77    ],
78    overflowBin: {min: 2200, max: Number.MAX_VALUE, count: 0}
79  });
80
81  var LOAD_HISTOGRAM = tr.v.Numeric.fromDict({
82    unit: 'unitless',
83    min: 1000,
84    max: 60000,
85    centralBinWidth: 5900,
86    underflowBin: {min: -Number.MAX_VALUE, max: 1000, count: 1000},
87    centralBins: [
88      {min: 1000, max: 6900, count: 901},
89      {min: 6900, max: 12800, count: 574},
90      {min: 12800, max: 18700, count: 298},
91      {min: 18700, max: 24600, count: 65},
92      {min: 24600, max: 30500, count: 35},
93      {min: 30500, max: 36400, count: 23},
94      {min: 36400, max: 42300, count: 16},
95      {min: 42300, max: 48200, count: 10},
96      {min: 48200, max: 54100, count: 5},
97      {min: 54100, max: 60000, count: 2}
98    ],
99    overflowBin: {min: 60000, max: Number.MAX_VALUE, count: 0}
100  });
101
102  var UNIT = tr.v.Unit.byName.normalizedPercentage_biggerIsBetter;
103
104  var DESCRIPTION = (
105      'For Load and Response, Mean Opinion Score of completion time; ' +
106      'For Animation, perceptual blend of Mean Opinion Scores of ' +
107      'throughput and smoothness');
108
109  function getDurationScore(histogram, duration) {
110    return histogram.getInterpolatedCountAt(duration) / histogram.maxCount;
111  }
112
113  function ResponsivenessMetric(valueList, model) {
114    tr.metrics.sh.AnimationThroughputMetric(valueList, model);
115    tr.metrics.sh.AnimationSmoothnessMetric(valueList, model);
116
117    var throughputForAnimation = {};
118    var smoothnessForAnimation = {};
119    valueList.valueDicts.forEach(function(value) {
120      if ((value.type !== 'numeric') ||
121          (value.numeric.type !== 'scalar'))
122        return;
123
124      var ue = value.grouping_keys.userExpectationStableId;
125
126      if (value.grouping_keys.name === 'throughput')
127        throughputForAnimation[ue] = value.numeric.value;
128      if (value.grouping_keys.name === 'smoothness')
129        smoothnessForAnimation[ue] = value.numeric.value;
130    });
131
132    var scores = [];
133
134    model.userModel.expectations.forEach(function(ue) {
135      var score = undefined;
136
137      if (ue instanceof tr.model.um.IdleExpectation) {
138        // Responsiveness is not defined for Idle.
139        return;
140      } else if (ue instanceof tr.model.um.LoadExpectation) {
141        score = getDurationScore(LOAD_HISTOGRAM, ue.duration);
142      } else if (ue instanceof tr.model.um.ResponseExpectation) {
143        var histogram = RESPONSE_HISTOGRAM;
144        if (ue.isAnimationBegin)
145          histogram = FAST_RESPONSE_HISTOGRAM;
146
147        score = getDurationScore(histogram, ue.duration);
148      } else if (ue instanceof tr.model.um.AnimationExpectation) {
149        var throughput = throughputForAnimation[ue.stableId];
150        var smoothness = smoothnessForAnimation[ue.stableId];
151
152        if (throughput === undefined)
153          throw new Error('Missing throughput for ' + ue.stableId);
154
155        if (smoothness === undefined)
156          throw new Error('Missing smoothness for ' + ue.stableId);
157
158        score = tr.b.Statistics.weightedMean(
159            [throughput, smoothness], tr.metrics.sh.perceptualBlend);
160      } else {
161        throw new Error('Unrecognized stage for ' + ue.stableId);
162      }
163
164      if (score === undefined)
165        throw new Error('Failed to compute responsiveness for ' + ue.stableId);
166
167      scores.push(score);
168
169      var options = {};
170      options.description = DESCRIPTION;
171
172      var groupingKeys = {};
173      groupingKeys.userExpectationStableId = ue.stableId;
174      groupingKeys.userExpectationStageTitle = ue.stageTitle;
175      groupingKeys.userExpectationInitiatorTitle = ue.initiatorTitle;
176
177      valueList.addValue(new tr.v.NumericValue(
178          model.canonicalUrlThatCreatedThisTrace, 'responsiveness',
179          new tr.v.ScalarNumeric(UNIT, score),
180          options, groupingKeys));
181    });
182
183    // Manually reduce scores.
184    // https://github.com/catapult-project/catapult/issues/2036
185
186    var options = {};
187    options.description = DESCRIPTION;
188    var groupingKeys = {};
189    var overallScore = tr.b.Statistics.weightedMean(
190        scores, tr.metrics.sh.perceptualBlend);
191    if (overallScore === undefined)
192      return;
193
194    valueList.addValue(new tr.v.NumericValue(
195        model.canonicalUrlThatCreatedThisTrace, 'responsiveness',
196        new tr.v.ScalarNumeric(UNIT, overallScore),
197        options, groupingKeys));
198  }
199
200  ResponsivenessMetric.prototype = {
201    __proto__: Function.prototype
202  };
203
204  tr.metrics.MetricRegistry.register(ResponsivenessMetric);
205
206  return {
207    ResponsivenessMetric: ResponsivenessMetric
208  };
209});
210</script>
211