• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2// Copyright (C) 2018 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16import * as m from 'mithril';
17
18import {Actions} from '../common/actions';
19import {globals} from './globals';
20import {PanelContainer} from './panel_container';
21
22/**
23 * Shorthand for if globals perf debug mode is on.
24 */
25export const perfDebug = () => globals.state.perfDebug;
26
27/**
28 * Returns performance.now() if perfDebug is enabled, otherwise 0.
29 * This is needed because calling performance.now is generally expensive
30 * and should not be done for every frame.
31 */
32export const debugNow = () => perfDebug() ? performance.now() : 0;
33
34/**
35 * Returns execution time of |fn| if perf debug mode is on. Returns 0 otherwise.
36 */
37export function measure(fn: () => void): number {
38  const start = debugNow();
39  fn();
40  return debugNow() - start;
41}
42
43/**
44 * Stores statistics about samples, and keeps a fixed size buffer of most recent
45 * samples.
46 */
47export class RunningStatistics {
48  private _count = 0;
49  private _mean = 0;
50  private _lastValue = 0;
51
52  private buffer: number[] = [];
53
54  constructor(private _maxBufferSize = 10) {}
55
56  addValue(value: number) {
57    this._lastValue = value;
58    this.buffer.push(value);
59    if (this.buffer.length > this._maxBufferSize) {
60      this.buffer.shift();
61    }
62    this._mean = (this._mean * this._count + value) / (this._count + 1);
63    this._count++;
64  }
65
66  get mean() {
67    return this._mean;
68  }
69  get count() {
70    return this._count;
71  }
72  get bufferMean() {
73    return this.buffer.reduce((sum, v) => sum + v, 0) / this.buffer.length;
74  }
75  get bufferSize() {
76    return this.buffer.length;
77  }
78  get maxBufferSize() {
79    return this._maxBufferSize;
80  }
81  get last() {
82    return this._lastValue;
83  }
84}
85
86/**
87 * Returns a summary string representation of a RunningStatistics object.
88 */
89export function runningStatStr(stat: RunningStatistics) {
90  return `Last: ${stat.last.toFixed(2)}ms | ` +
91      `Avg: ${stat.mean.toFixed(2)}ms | ` +
92      `Avg${stat.maxBufferSize}: ${stat.bufferMean.toFixed(2)}ms`;
93}
94
95/**
96 * Globals singleton class that renders performance stats for the whole app.
97 */
98class PerfDisplay {
99  private containers: PanelContainer[] = [];
100  addContainer(container: PanelContainer) {
101    this.containers.push(container);
102  }
103
104  removeContainer(container: PanelContainer) {
105    const i = this.containers.indexOf(container);
106    this.containers.splice(i, 1);
107  }
108
109  renderPerfStats() {
110    if (!perfDebug()) return;
111    const perfDisplayEl = document.querySelector('.perf-stats');
112    if (!perfDisplayEl) return;
113    m.render(perfDisplayEl, [
114      m('section', globals.rafScheduler.renderPerfStats()),
115      m('button.close-button',
116        {
117          onclick: () => globals.dispatch(Actions.togglePerfDebug({})),
118        },
119        m('i.material-icons', 'close')),
120      this.containers.map((c, i) => m('section', c.renderPerfStats(i)))
121    ]);
122  }
123}
124
125export const perfDisplay = new PerfDisplay();
126