• 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 m from 'mithril';
17
18import {Actions} from '../common/actions';
19import {globals} from './globals';
20import {PanelContainer} from './panel_container';
21
22// Shorthand for if globals perf debug mode is on.
23export const perfDebug = () => globals.state.perfDebug;
24
25// Returns performance.now() if perfDebug is enabled, otherwise 0.
26// This is needed because calling performance.now is generally expensive
27// and should not be done for every frame.
28export const debugNow = () => perfDebug() ? performance.now() : 0;
29
30// Returns execution time of |fn| if perf debug mode is on. Returns 0 otherwise.
31export function measure(fn: () => void): number {
32  const start = debugNow();
33  fn();
34  return debugNow() - start;
35}
36
37// Stores statistics about samples, and keeps a fixed size buffer of most recent
38// samples.
39export class RunningStatistics {
40  private _count = 0;
41  private _mean = 0;
42  private _lastValue = 0;
43  private _ptr = 0;
44
45  private buffer: number[] = [];
46
47  constructor(private _maxBufferSize = 10) {}
48
49  addValue(value: number) {
50    this._lastValue = value;
51    if (this.buffer.length >= this._maxBufferSize) {
52      this.buffer[this._ptr++] = value;
53      if (this._ptr >= this.buffer.length) {
54        this._ptr -= this.buffer.length;
55      }
56    } else {
57      this.buffer.push(value);
58    }
59
60    this._mean = (this._mean * this._count + value) / (this._count + 1);
61    this._count++;
62  }
63
64  get mean() {
65    return this._mean;
66  }
67  get count() {
68    return this._count;
69  }
70  get bufferMean() {
71    return this.buffer.reduce((sum, v) => sum + v, 0) / this.buffer.length;
72  }
73  get bufferSize() {
74    return this.buffer.length;
75  }
76  get maxBufferSize() {
77    return this._maxBufferSize;
78  }
79  get last() {
80    return this._lastValue;
81  }
82}
83
84// Returns a summary string representation of a RunningStatistics object.
85export function runningStatStr(stat: RunningStatistics) {
86  return `Last: ${stat.last.toFixed(2)}ms | ` +
87      `Avg: ${stat.mean.toFixed(2)}ms | ` +
88      `Avg${stat.maxBufferSize}: ${stat.bufferMean.toFixed(2)}ms`;
89}
90
91// Globals singleton class that renders performance stats for the whole app.
92class PerfDisplay {
93  private containers: PanelContainer[] = [];
94  addContainer(container: PanelContainer) {
95    this.containers.push(container);
96  }
97
98  removeContainer(container: PanelContainer) {
99    const i = this.containers.indexOf(container);
100    this.containers.splice(i, 1);
101  }
102
103  renderPerfStats() {
104    if (!perfDebug()) return;
105    const perfDisplayEl = document.querySelector('.perf-stats');
106    if (!perfDisplayEl) return;
107    m.render(perfDisplayEl, [
108      m('section', globals.rafScheduler.renderPerfStats()),
109      m('button.close-button',
110        {
111          onclick: () => globals.dispatch(Actions.togglePerfDebug({})),
112        },
113        m('i.material-icons', 'close')),
114      this.containers.map((c, i) => m('section', c.renderPerfStats(i))),
115    ]);
116  }
117}
118
119export const perfDisplay = new PerfDisplay();
120