tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

perf.js (5709B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // Performance monitoring and calculation.
      6 
      7 function round_up(val, interval) {
      8  return val + (interval - (val % interval));
      9 }
     10 
     11 // Class for inter-frame timing, which handles being paused and resumed.
     12 var FrameTimer = class {
     13  constructor() {
     14    // Start time of the current active test, adjusted for any time spent
     15    // stopped (so `now - this.start` is how long the current active test
     16    // has run for.)
     17    this.start = undefined;
     18 
     19    // Timestamp of callback following the previous frame.
     20    this.prev = undefined;
     21 
     22    // Timestamp when drawing was paused, or zero if drawing is active.
     23    this.stopped = 0;
     24  }
     25 
     26  is_stopped() {
     27    return this.stopped != 0;
     28  }
     29 
     30  start_recording(now = gHost.now()) {
     31    this.start = this.prev = now;
     32  }
     33 
     34  on_frame_finished(now = gHost.now()) {
     35    const delay = now - this.prev;
     36    this.prev = now;
     37    return delay;
     38  }
     39 
     40  pause(now = gHost.now()) {
     41    this.stopped = now;
     42    // Abuse this.prev to store the time elapsed since the previous frame.
     43    // This will be used to adjust this.prev when we resume.
     44    this.prev = now - this.prev;
     45  }
     46 
     47  resume(now = gHost.now()) {
     48    this.prev = now - this.prev;
     49    const stop_duration = now - this.stopped;
     50    this.start += stop_duration;
     51    this.stopped = 0;
     52  }
     53 };
     54 
     55 // Per-frame time sampling infra.
     56 var sampleTime = 16.666667; // ms
     57 var sampleIndex = 0;
     58 
     59 // Class for maintaining a rolling window of per-frame GC-related counters:
     60 // inter-frame delay, minor/major/slice GC counts, cumulative bytes, etc.
     61 var FrameHistory = class {
     62  constructor(numSamples) {
     63    // Private
     64    this._frameTimer = new FrameTimer();
     65    this._numSamples = numSamples;
     66 
     67    // Public API
     68    this.delays = new Array(numSamples);
     69    this.gcBytes = new Array(numSamples);
     70    this.mallocBytes = new Array(numSamples);
     71    this.gcs = new Array(numSamples);
     72    this.minorGCs = new Array(numSamples);
     73    this.majorGCs = new Array(numSamples);
     74    this.slices = new Array(numSamples);
     75 
     76    sampleIndex = 0;
     77    this.reset();
     78  }
     79 
     80  start(now = gHost.now()) {
     81    this._frameTimer.start_recording(now);
     82  }
     83 
     84  reset() {
     85    this.delays.fill(0);
     86    this.gcBytes.fill(0);
     87    this.mallocBytes.fill(0);
     88    this.gcs.fill(this.gcs[sampleIndex]);
     89    this.minorGCs.fill(this.minorGCs[sampleIndex]);
     90    this.majorGCs.fill(this.majorGCs[sampleIndex]);
     91    this.slices.fill(this.slices[sampleIndex]);
     92 
     93    sampleIndex = 0;
     94  }
     95 
     96  get numSamples() {
     97    return this._numSamples;
     98  }
     99 
    100  findMax(collection) {
    101    // Depends on having at least one non-negative entry, and unfilled
    102    // entries being <= max.
    103    var maxIndex = 0;
    104    for (let i = 0; i < this._numSamples; i++) {
    105      if (collection[i] >= collection[maxIndex]) {
    106        maxIndex = i;
    107      }
    108    }
    109    return maxIndex;
    110  }
    111 
    112  findMaxDelay() {
    113    return this.findMax(this.delays);
    114  }
    115 
    116  on_frame(now = gHost.now()) {
    117    const delay = this._frameTimer.on_frame_finished(now);
    118 
    119    // Total time elapsed while the active test has been running.
    120    var t = now - this._frameTimer.start;
    121    var newIndex = Math.round(t / sampleTime);
    122    while (sampleIndex < newIndex) {
    123      sampleIndex++;
    124      var idx = sampleIndex % this._numSamples;
    125      this.delays[idx] = delay;
    126      if (gHost.features.haveMemorySizes) {
    127        this.gcBytes[idx] = gHost.gcBytes;
    128        this.mallocBytes[idx] = gHost.mallocBytes;
    129      }
    130      if (gHost.features.haveGCCounts) {
    131        this.minorGCs[idx] = gHost.minorGCCount;
    132        this.majorGCs[idx] = gHost.majorGCCount;
    133        this.slices[idx] = gHost.GCSliceCount;
    134      }
    135    }
    136 
    137    return delay;
    138  }
    139 
    140  pause() {
    141    this._frameTimer.pause();
    142  }
    143 
    144  resume() {
    145    this._frameTimer.resume();
    146  }
    147 
    148  is_stopped() {
    149    return this._frameTimer.is_stopped();
    150  }
    151 };
    152 
    153 var PerfTracker = class {
    154  constructor() {
    155    // Private
    156    this._currentLoadStart = undefined;
    157    this._frameCount = undefined;
    158    this._mutating_ms = undefined;
    159    this._suspend_sec = undefined;
    160    this._minorGCs = undefined;
    161    this._majorGCs = undefined;
    162 
    163    // Public
    164    this.results = [];
    165  }
    166 
    167  on_load_start(load, now = gHost.now()) {
    168    this._currentLoadStart = now;
    169    this._frameCount = 0;
    170    this._mutating_ms = 0;
    171    this._suspend_sec = 0;
    172    this._majorGCs = gHost.majorGCCount;
    173    this._minorGCs = gHost.minorGCCount;
    174  }
    175 
    176  on_load_end(load, now = gHost.now()) {
    177    const elapsed_time = (now - this._currentLoadStart) / 1000;
    178    const full_time = round_up(elapsed_time, 1 / 60);
    179    const frame_60fps_limit = Math.round(full_time * 60);
    180    const dropped_60fps_frames = frame_60fps_limit - this._frameCount;
    181    const dropped_60fps_fraction = dropped_60fps_frames / frame_60fps_limit;
    182 
    183    const mutating_and_gc_fraction = this._mutating_ms / (full_time * 1000);
    184 
    185    const result = {
    186      load,
    187      elapsed_time,
    188      mutating: this._mutating_ms / 1000,
    189      mutating_and_gc_fraction,
    190      suspended: this._suspend_sec,
    191      full_time,
    192      frames: this._frameCount,
    193      dropped_60fps_frames,
    194      dropped_60fps_fraction,
    195      majorGCs: gHost.majorGCCount - this._majorGCs,
    196      minorGCs: gHost.minorGCCount - this._minorGCs,
    197    };
    198    this.results.push(result);
    199 
    200    this._currentLoadStart = undefined;
    201    this._frameCount = 0;
    202 
    203    return result;
    204  }
    205 
    206  after_suspend(wait_sec) {
    207    this._suspend_sec += wait_sec;
    208  }
    209 
    210  before_mutator(now = gHost.now()) {
    211    this._frameCount++;
    212  }
    213 
    214  after_mutator(start_time, end_time = gHost.now()) {
    215    this._mutating_ms += end_time - start_time;
    216  }
    217 };