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