| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 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. |
| --> |
| <link rel="import" href="/tracing/base/base.html"> |
| <link rel="import" href="/tracing/base/statistics.html"> |
| <link rel="import" href="/tracing/core/auditor.html"> |
| <link rel="import" href="/tracing/model/model.html"> |
| <link rel="import" href="/tracing/base/range_utils.html"> |
| <link rel="import" href="/tracing/extras/chrome/chrome_model_helper.html"> |
| |
| <script> |
| 'use strict'; |
| |
| /** |
| * @fileoverview Base class for trace data Auditors. |
| */ |
| tr.exportTo('tr.e.rail', function() { |
| |
| // We need an up-front list of all IR types in order to keep various groupings |
| // stable, in presence of only a portion of interactions in a given trace. |
| var ALL_RAIL_TYPE_NAMES = [ |
| 'rail_response', |
| 'rail_animate', |
| 'rail_idle', |
| 'rail_load' |
| ]; |
| |
| var DOES_RAIL_TYPE_NAME_EXIST = {}; |
| ALL_RAIL_TYPE_NAMES.forEach(function(railTypeName) { |
| DOES_RAIL_TYPE_NAME_EXIST[railTypeName] = true; |
| }); |
| |
| |
| function RAILInteractionRecord(title, railTypeName, start, duration) { |
| if (!DOES_RAIL_TYPE_NAME_EXIST[railTypeName]) |
| throw new Error(railTypeName + ' is not listed in ALL_RAIL_TYPE_NAMES'); |
| |
| var colorId = tr.ui.b.getColorIdForReservedName(railTypeName); |
| this.railTypeName_ = railTypeName; |
| this.name = ''; |
| tr.model.InteractionRecord.call(this, |
| title, colorId, start, duration); |
| } |
| |
| RAILInteractionRecord.prototype = { |
| __proto__: tr.model.InteractionRecord.prototype, |
| |
| updateArgs: function() { |
| var args = {}; |
| |
| var layoutSlices = this.associatedEvents.filter(function(event) { |
| return event.title === 'FrameView::layout'; |
| }); |
| var timeInLayout = tr.b.Statistics.sum(layoutSlices, function(event) { |
| return event.duration; |
| }); |
| |
| args['layoutInfo'] = { |
| 'timeInLayout': timeInLayout |
| }; |
| |
| this.args = args; |
| }, |
| |
| get railTypeName() { |
| return this.railTypeName_; |
| }, |
| |
| /** |
| * Returns the overall rail score, from 0 to 1. |
| * |
| * RAILScore for an interaction merges the user's pain with the |
| * efficiency, in order to create a perception-oriented measure |
| * of how users percieve speed during this interaction. |
| * |
| * 0 means a bad user experience. |
| * 1 means a perfect user experience. |
| */ |
| get railScore() { |
| var happiness = 1 - this.normalizedUserPain; |
| var efficiency = this.normalizedEfficiency; |
| // The lower sub-score is more important than the higher sub-score. |
| // happiness is more important than efficiency. |
| var happinessWeight = 2 * Math.exp(1 - happiness); |
| var efficiencyWeight = Math.exp(1 - efficiency); |
| // This function is graphed at http://goo.gl/XMWUKA |
| return ((happiness * happinessWeight + efficiency * efficiencyWeight) / |
| (happinessWeight + efficiencyWeight)); |
| }, |
| |
| /** |
| * Measures the pain the user experienced, from 0 to 1. |
| * |
| * A user performs an interaction with an expectation in mind. |
| * When we exceed their expectations, we get zero pain. |
| * When we meet their expectations, we get zero pain. |
| * As we exceed their expectations, pain goes up. Maximum pain |
| * is 1.0, aka "Switch to FireFox". |
| */ |
| get normalizedUserPain() { |
| throw new Error('Not implemented'); |
| }, |
| |
| /** |
| * Returns the sum of the number of CPU ms spent by this IR. |
| */ |
| get rawCpuMs() { |
| var cpuMs = 0; |
| this.associatedEvents.forEach(function(event) { |
| if (event.cpuSelfTime) |
| cpuMs += event.cpuSelfTime; |
| }); |
| return cpuMs; |
| }, |
| |
| /** |
| * Returns a number between 0 and 1 representing how efficiently this IR |
| * used CPU resources. 0 is maximally in-efficient, 1 is maximally |
| * efficient. |
| */ |
| get normalizedCpuEfficiency() { |
| var minCpuMs = this.duration * this.minCpuFraction; |
| var maxCpuMs = this.duration * this.maxCpuFraction; |
| var normalizedCpu = tr.b.normalize(this.rawCpuMs, minCpuMs, maxCpuMs); |
| return 1 - tr.b.clamp(normalizedCpu, 0, 1); |
| }, |
| |
| /** |
| * The minimum fraction of a CPU that can be spent on this IR before the |
| * efficiency score will be impacted. |
| * If less CPU ms than this is spent on this IR, then |
| * normalizedCpuEfficiency will be 1. |
| */ |
| get minCpuFraction() { |
| return 0.5; |
| }, |
| |
| /** |
| * The maximum fraction of a CPU that can be spent on this IR. |
| * If more CPU ms than this is spent on this IR, then |
| * normalizedCpuEfficiency will be 0. |
| */ |
| get maxCpuFraction() { |
| return 1.5; |
| }, |
| |
| /** |
| * Measures the efficiency of the interaction from 0 to 1. |
| * |
| * Efficiency is a notion of how well we used the machine's limited |
| * resources in service of this interaction. If we used it perfectly, |
| * we would get a 1.0. If we used everything that there was to use --- |
| * power, memory, cpu, then we'd get a zero. |
| */ |
| get normalizedEfficiency() { |
| return this.normalizedCpuEfficiency; |
| } |
| }; |
| |
| return { |
| RAILInteractionRecord: RAILInteractionRecord, |
| ALL_RAIL_TYPE_NAMES: ALL_RAIL_TYPE_NAMES |
| }; |
| }); |
| </script> |